blob: 155fdf40199ec4bf6fe4489aef696d8e1c13a3aa [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,
26 typename HalOperation = typename HalPolicy::Operation,
27 typename HalModel = typename HalPolicy::Model>
28bool IsQSymmDequantizeForWeights(const HalOperation& operation, const HalModel& model)
29{
30 using HalOperand = typename HalPolicy::Operand;
31 using HalOperationType = typename HalPolicy::OperationType;
32
33 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, 0, model);
34 if (!operand)
35 {
36 return false;
37 }
38
39 if(!IsQSymm8(*operand))
40 {
41 // Only QSymm8 weights are dequantized on the fly by the driver
42 return false;
43 }
44
45 if (!IsOperandConstant<HalPolicy>(*operand))
46 {
47 // Non-const input is not accepted for weights
48 return false;
49 }
50
51 // Iterate through all the operations and find the operation feeding from the Dequantize output
52 const size_t outputIndex = operation.outputs[0];
53 for (uint32_t operationIdx = 0; operationIdx < getMainModel(model).operations.size(); ++operationIdx)
54 {
55 const auto& operationIt = getMainModel(model).operations[operationIdx];
56 switch (operationIt.type)
57 {
58 case HalOperationType::FULLY_CONNECTED:
59 if (outputIndex == operationIt.inputs[1]) // Weights are bound to slot 1
60 {
61 // If the output is going into the FC weights return true
62 return true;
63 }
64 break;
65 case HalOperationType::LSTM:
66 for (size_t k = 0; k < operationIt.inputs.size(); ++k)
67 {
68 if (outputIndex == operationIt.inputs[k])
69 {
70 // If the output is going into the LSTM weights return true
71 return true;
72 }
73 }
74 break;
75 default:
76 break;
77 }
78 }
79
80 return false;
81}
82
83template<typename HalPolicy,
84 typename HalOperation = typename HalPolicy::Operation,
85 typename HalModel = typename HalPolicy::Model>
86bool SetupAndTrackLayerOutputSlotAndOverrideTensorInfo(const HalOperation& operation,
87 uint32_t operationOutputIndex,
88 armnn::IConnectableLayer& layer,
89 uint32_t layerOutputIndex,
90 const HalModel& model,
91 ConversionData& data,
92 const armnn::TensorInfo tensor_info)
93{
94 using HalOperand = typename HalPolicy::Operand;
95
96 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
97 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
98 {
99 return false;
100 }
101
102 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
103
104 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
105 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
106
107 outputSlot.SetTensorInfo(tensor_info);
108
109 return true;
110}
111
112template<typename HalPolicy,
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100113 typename HalOperation = typename HalPolicy::Operation,
114 typename HalModel = typename HalPolicy::Model>
115bool ConvertCast(const HalOperation& operation,
116 const HalModel& model,
117 ConversionData& data)
118{
119 using HalOperand = typename HalPolicy::Operand;
120
121 ALOGV("HalPolicy::ConvertCast()");
122
123 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
124
125 if (!input.IsValid())
126 {
127 return Fail("%s: Operation has invalid inputs", __func__);
128 }
129
130 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
131 if (!output)
132 {
133 return Fail("%s: Could not read output 0", __func__);
134 }
135
136 const TensorInfo& inputInfo = input.GetTensorInfo();
137 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
138
139 bool isSupported = false;
140
141 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
142 {
143 FORWARD_LAYER_SUPPORT_FUNC(__func__,
144 IsCastSupported,
145 data.m_Backends,
146 isSupported,
147 inputInfo,
148 outputInfo);
149 };
150
151 if(!IsDynamicTensor(outputInfo))
152 {
153 validateFunc(outputInfo, isSupported);
154 }
155 else
156 {
157 isSupported = AreDynamicTensorsSupported();
158 }
159
160 if (!isSupported)
161 {
162 return false;
163 }
164
165 IConnectableLayer* layer = data.m_Network->AddCastLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +0100166 if (!layer)
167 {
168 return Fail("%s: Could not add the CastLayer", __func__);
169 }
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100170 input.Connect(layer->GetInputSlot(0));
171
172 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
173}
174
175template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000176 typename HalOperation = typename HalPolicy::Operation,
177 typename HalModel = typename HalPolicy::Model>
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100178bool ConvertChannelShuffle(const HalOperation& operation,
179 const HalModel& model,
180 ConversionData& data)
181{
182 using HalOperand = typename HalPolicy::Operand;
183 using HalOperandType = typename HalPolicy::OperandType;
184
185 ALOGV("HalPolicy::ConvertChannelShuffle()");
186
187 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
188 if (!input.IsValid())
189 {
190 return Fail("%s: Operation has invalid inputs", __func__);
191 }
192 auto inputDimensions = static_cast<int32_t>(input.GetTensorInfo().GetNumDimensions());
193
194 ChannelShuffleDescriptor descriptor;
195
196 int32_t groups;
197 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, groups, model, data))
198 {
199 return Fail("%s: Operation has invalid or unsupported number of groups operand", __func__);
200 }
201 descriptor.m_NumGroups = static_cast<uint32_t>(groups);
202
203 int32_t axis;
204 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, axis, model, data))
205 {
206 return Fail("%s: Operation has invalid or unsupported dimension channel shuffle operand", __func__);
207 }
208 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
209 {
210 return Fail("%s: Operation has invalid dimension: %d. It is out of bounds [-%d, %d))", __func__, axis,
211 inputDimensions, inputDimensions);
212 }
213 int positiveAxis = (axis < 0) ? inputDimensions + axis : axis;
214 descriptor.m_Axis = static_cast<uint32_t>(positiveAxis);
215
216 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
217 if (!output)
218 {
219 return Fail("%s: Could not read output 0", __func__);
220 }
221
222 const TensorInfo& inputInfo = input.GetTensorInfo();
223 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
224
225 bool isSupported = false;
226
227 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
228 {
229 FORWARD_LAYER_SUPPORT_FUNC(__func__,
230 IsChannelShuffleSupported,
231 data.m_Backends,
232 isSupported,
233 inputInfo,
234 outputInfo,
235 descriptor);
236 };
237
238 if(!IsDynamicTensor(outputInfo))
239 {
240 validateFunc(outputInfo, isSupported);
241 }
242 else
243 {
244 isSupported = AreDynamicTensorsSupported();
245 }
246
247 if (!isSupported)
248 {
249 return false;
250 }
251
252 IConnectableLayer* layer = data.m_Network->AddChannelShuffleLayer(descriptor);
253 assert(layer != nullptr);
254 input.Connect(layer->GetInputSlot(0));
255
256 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
257}
258
259template<typename HalPolicy,
260 typename HalOperation = typename HalPolicy::Operation,
261 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +0000262bool ConvertComparison_1_2(const HalOperation& operation,
263 const HalModel& model,
264 ConversionData& data,
265 ComparisonOperation comparisonOperation)
266{
267 using HalOperand = typename HalPolicy::Operand;
268
269 ALOGV("HalPolicy::ConvertComparison()");
270 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
271
272 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
273 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
274
275 if (!(input0.IsValid() && input1.IsValid()))
276 {
277 return Fail("%s: Operation has invalid inputs", __func__);
278 }
279
280 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
281 if (!output)
282 {
283 return Fail("%s: Could not read output 0", __func__);
284 }
285
286 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
287 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
288 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
289
Kevin May42477c12020-03-26 13:34:14 +0000290 ComparisonDescriptor descriptor(comparisonOperation);
291
292 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100293 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
294 {
295 FORWARD_LAYER_SUPPORT_FUNC(__func__,
296 IsComparisonSupported,
297 data.m_Backends,
298 isSupported,
299 inputInfo0,
300 inputInfo1,
301 outputInfo,
302 descriptor);
303
304 };
305
306 if(!IsDynamicTensor(outputInfo))
307 {
308 validateFunc(outputInfo, isSupported);
309 }
310 else
311 {
312 isSupported = AreDynamicTensorsSupported();
313 }
Kevin May42477c12020-03-26 13:34:14 +0000314
315 if (!isSupported)
316 {
317 return false;
318 }
319
320 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +0100321 if (!layer)
322 {
323 return Fail("%s: Could not add the ComparisonLayer", __func__);
324 }
Kevin May42477c12020-03-26 13:34:14 +0000325
326 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
327 if (!isReshapeSupported)
328 {
329 return false;
330 }
331
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100332 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000333}
334
335template<typename HalPolicy,
336 typename HalOperation = typename HalPolicy::Operation,
337 typename HalModel = typename HalPolicy::Model>
338bool ConvertConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
339{
340
341 using HalOperand = typename HalPolicy::Operand;
342 using HalOperandType = typename HalPolicy::OperandType;
343
344 ALOGV("HalPolicy::ConvertConv2d_1_2()");
345
346 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
347 if (!input.IsValid())
348 {
349 return Fail("%s: Operation has invalid inputs", __func__);
350 }
351
352 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
353 if (!output)
354 {
355 return Fail("%s: Could not read output 0", __func__);
356 }
357
358 const TensorInfo& inputInfo = input.GetTensorInfo();
359 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
360
Kevin May42477c12020-03-26 13:34:14 +0000361 Convolution2dDescriptor desc;
362 desc.m_DataLayout = DataLayout::NHWC;
363
364 // Determine whether padding is implicit or explicit
365 bool implicitPadding = operation.inputs.size() == 7 ||
366 (operation.inputs.size() >= 8 &&
367 GetInputOperand<HalPolicy>(operation, 7, model)->type == HalOperandType::BOOL);
368
369 if (implicitPadding)
370 {
371 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
372 }
373 else if (operation.inputs.size() >= 10)
374 {
375 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
376 }
377
378 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
379
380 // ArmNN does not currently support non-fixed weights or bias
381 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
382 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
383 // the DataLayout is NCHW
384 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
385 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
386 model, data, OHWIToOIHW) :
387 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
388 const ConstTensorPin biasPin =
389 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
390
391 if (!weightsPin.IsValid())
392 {
393 return Fail("%s: Operation has invalid weights", __func__);
394 }
395
396 if (!biasPin.IsValid())
397 {
398 return Fail("%s: Operation has invalid biases", __func__);
399 }
400
401 ConstTensor weights = weightsPin.GetConstTensor();
402 ConstTensor bias = biasPin.GetConstTensor();
403 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
404
405 ActivationFn activation;
406
407 if (implicitPadding)
408 {
409 android::nn::PaddingScheme paddingScheme;
410 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
411 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
412 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
413 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data) ||
414 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 8, desc, model, data))
415 {
416 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
417 }
418
419 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
420 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
421 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
422 const uint32_t kernelX = weights.GetShape()[widthIndex];
423 const uint32_t kernelY = weights.GetShape()[heightIndex];
424 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
425 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
426
427 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
428 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
429
430 }
431 else if (operation.inputs.size() >= 10)
432 {
433 // explicit padding
434 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
435 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
436 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
437 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
438 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
439 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
440 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data) ||
441 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 11, desc, model, data))
442 {
443 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
444 }
445 }
446 else
447 {
448 return Fail("%s: Unsupported number of operation inputs", __func__);
449 }
450
451 desc.m_BiasEnabled = true;
452 Optional<TensorInfo> biases(bias.GetInfo());
453
454 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100455 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
456 {
457 FORWARD_LAYER_SUPPORT_FUNC(__func__,
458 IsConvolution2dSupported,
459 data.m_Backends,
460 isSupported,
461 inputInfo,
462 outputInfo,
463 desc,
464 weights.GetInfo(),
465 biases);
466 };
467
468 if(!IsDynamicTensor(outputInfo))
469 {
470 validateFunc(outputInfo, isSupported);
471 }
472 else
473 {
474 isSupported = AreDynamicTensorsSupported();
475 }
Kevin May42477c12020-03-26 13:34:14 +0000476
477 if (!isSupported)
478 {
479 return false;
480 }
481
482 IConnectableLayer* startLayer =
483 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
484
485 if (!startLayer)
486 {
487 return Fail("%s: AddConvolution2dLayer failed", __func__);
488 }
489
Kevin May42477c12020-03-26 13:34:14 +0000490 input.Connect(startLayer->GetInputSlot(0));
491
Kevin Mayfcf2a152020-09-08 16:06:32 +0100492 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
493 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000494}
495
496template<typename HalPolicy,
497 typename HalOperation = typename HalPolicy::Operation,
498 typename HalModel = typename HalPolicy::Model>
499bool ConvertDepthwiseConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
500{
501 using HalOperand = typename HalPolicy::Operand;
502 using HalOperandType = typename HalPolicy::OperandType;
503
504 ALOGV("HalPolicy::ConvertDepthwiseConv2d_1_2()");
505
506 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
507
508 if (!input.IsValid())
509 {
510 return Fail("%s: Operation has invalid inputs", __func__);
511 }
512
513 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
514
515 if (!output)
516 {
517 return Fail("%s: Could not read output 0", __func__);
518 }
519
520 const TensorInfo& inputInfo = input.GetTensorInfo();
521 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
522
Kevin May42477c12020-03-26 13:34:14 +0000523 // ArmNN does not currently support non-fixed weights or bias
524 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
525 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
526
527 if (weightsOperand == nullptr)
528 {
529 return Fail("%s: Operand is invalid", __func__);
530 }
531 if ( weightsOperand->dimensions[0] != 1)
532 {
533 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
534 __func__, weightsOperand->dimensions[0] );
535 }
536
537 DepthwiseConvolution2dDescriptor desc;
538 desc.m_DataLayout = DataLayout::NHWC;
539
540 // Determine whether padding is implicit or explicit
541 bool implicitPadding = operation.inputs.size() == 8 ||
542 (operation.inputs.size() >= 9 &&
543 GetInputOperand<HalPolicy>(operation, 8, model)->type == HalOperandType::BOOL);
544
545 // Look ahead to find the optional DataLayout, if present
546 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
547 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, dataLayoutFlagIndex, model, data);
548
549 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
Kevin May42477c12020-03-26 13:34:14 +0000550 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
551 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
552
Jan Eilersa20d2b82021-04-27 09:21:08 +0100553 // The layout for weights in depthwise is [ 1, H, W, O] and it's the same in ArmNN. No need to permute anything.
Kevin May42477c12020-03-26 13:34:14 +0000554 const ConstTensorPin weightsPin =
555 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
556 1,
557 model,
Jan Eilersa20d2b82021-04-27 09:21:08 +0100558 data);
Kevin May42477c12020-03-26 13:34:14 +0000559
560 // Bias is a 1D tensor
561 const ConstTensorPin biasPin =
562 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
563
564 if (!weightsPin.IsValid())
565 {
566 return Fail("%s: Operation has invalid weights", __func__);
567 }
568
569 if (!biasPin.IsValid())
570 {
571 return Fail("%s: Operation has invalid biases", __func__);
572 }
573
574 ConstTensor weights = weightsPin.GetConstTensor();
575 ConstTensor bias = biasPin.GetConstTensor();
576 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
577
578 ActivationFn activation;
579
580 if (implicitPadding)
581 {
582 android::nn::PaddingScheme paddingScheme;
583 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
584 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
585 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
586 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data) ||
587 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 9, desc, model, data))
588 {
589 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
590 }
591
Jan Eilersa20d2b82021-04-27 09:21:08 +0100592 const uint32_t kernelX = weights.GetShape()[2];
593 const uint32_t kernelY = weights.GetShape()[1];
Kevin May42477c12020-03-26 13:34:14 +0000594 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
595 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
596
597 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
598 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
599 }
600 else if (operation.inputs.size() >= 11)
601 {
602 // explicit padding
603 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
604 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
605 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
606 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
607 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
608 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
609 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data) ||
610 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 12, desc, model, data))
611 {
612 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
613 }
614 }
615 else
616 {
617 return Fail("%s: Unsupported number of operation inputs", __func__);
618 }
619
620 desc.m_BiasEnabled = true;
621 Optional<TensorInfo> biases(bias.GetInfo());
622
623 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100624 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
625 {
626 FORWARD_LAYER_SUPPORT_FUNC(__func__,
627 IsDepthwiseConvolutionSupported,
628 data.m_Backends,
629 isSupported,
630 inputInfo,
631 outputInfo,
632 desc,
633 weights.GetInfo(),
634 biases);
635 };
636
637 if(!IsDynamicTensor(outputInfo))
638 {
639 validateFunc(outputInfo, isSupported);
640 }
641 else
642 {
643 isSupported = AreDynamicTensorsSupported();
644 }
Kevin May42477c12020-03-26 13:34:14 +0000645
646 if (!isSupported)
647 {
648 return false;
649 }
650
651 IConnectableLayer* startLayer =
652 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
653
654 if (!startLayer)
655 {
656 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
657 }
658
Kevin May42477c12020-03-26 13:34:14 +0000659 input.Connect(startLayer->GetInputSlot(0));
660
Kevin Mayfcf2a152020-09-08 16:06:32 +0100661 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
662 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000663}
664
665template<typename HalPolicy,
666 typename HalOperation = typename HalPolicy::Operation,
667 typename HalModel = typename HalPolicy::Model>
668bool ConvertDequantize_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
669{
670 ALOGV("HalPolicy::ConvertDequantize()");
671
672 if (IsQSymmDequantizeForWeights<HalPolicy>(operation, model))
673 {
674 // NOTE: QSymm8 weights are dequantized internally by the driver,
675 // therefore this type of Dequantize is implicitly supported
676 return true;
677 }
678
679 return ::ConvertDequantize<HalPolicy>(operation, model, data);
680}
681
682template<typename HalPolicy,
683 typename HalOperation = typename HalPolicy::Operation,
684 typename HalModel = typename HalPolicy::Model>
685bool ConvertElementwiseUnary(const HalOperation& operation,
686 const HalModel& model,
687 ConversionData& data,
688 UnaryOperation unaryOperation)
689{
690 using HalOperand = typename HalPolicy::Operand;
691
692 ALOGV("HalPolicy::ConvertElementwiseUnary()");
693 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
694
695 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
696
697 if (!input.IsValid())
698 {
699 return Fail("%s: Operation has invalid input", __func__);
700 }
701
702 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
703 if (!output)
704 {
705 return Fail("%s: Could not read output 0", __func__);
706 }
707
708 const TensorInfo& inputInfo = input.GetTensorInfo();
709 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
710
Kevin May42477c12020-03-26 13:34:14 +0000711 ElementwiseUnaryDescriptor descriptor(unaryOperation);
712
713 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100714
715 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
716 {
717 FORWARD_LAYER_SUPPORT_FUNC(__func__,
718 IsElementwiseUnarySupported,
719 data.m_Backends,
720 isSupported,
721 inputInfo,
722 outputInfo,
723 descriptor);
724 };
725
726 if(!IsDynamicTensor(outputInfo))
727 {
728 validateFunc(outputInfo, isSupported);
729 }
730 else
731 {
732 isSupported = AreDynamicTensorsSupported();
733 }
Kevin May42477c12020-03-26 13:34:14 +0000734
735 if (!isSupported)
736 {
737 return false;
738 }
739
740 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +0100741 if (!layer)
742 {
743 return Fail("%s: Could not add the ElementwiseUnaryLayer", __func__);
744 }
Kevin May42477c12020-03-26 13:34:14 +0000745 input.Connect(layer->GetInputSlot(0));
746
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100747 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000748}
749
750template<typename HalPolicy,
751 typename HalOperation = typename HalPolicy::Operation,
752 typename HalModel = typename HalPolicy::Model>
753bool ConvertExpandDims(const HalOperation& operation, const HalModel& model, ConversionData& data)
754{
755 using HalOperand = typename HalPolicy::Operand;
756 using HalOperandType = typename HalPolicy::OperandType;
757
758 ALOGV("HalPolicy::ConvertExpandDims()");
759
760 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
761
762 if (!input.IsValid())
763 {
764 return Fail("%s: Operation has invalid input", __func__);
765 }
766
767 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
768 if (!output)
769 {
770 return Fail("%s: Operation has invalid output", __func__);
771 }
772
773 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000774
775 int32_t axis;
776 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
777 {
778 return Fail("%s: failed to get axis input value", __func__);
779 }
780
781 TensorShape targetShape;
782
783 try
784 {
785 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
786 }
787 catch (const std::exception& e)
788 {
789 return Fail("%s: %s", __func__, e.what());
790 }
791
Kevin May42477c12020-03-26 13:34:14 +0000792 ReshapeDescriptor reshapeDescriptor;
793 reshapeDescriptor.m_TargetShape = targetShape;
794
795 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100796 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
797 {
798 FORWARD_LAYER_SUPPORT_FUNC(__func__,
799 IsReshapeSupported,
800 data.m_Backends,
801 isSupported,
802 input.GetTensorInfo(),
803 outputInfo,
804 reshapeDescriptor);
805 };
806
807 if(!IsDynamicTensor(outputInfo))
808 {
Nikhil Rajc5e0bb02021-04-02 10:02:16 +0100809 if (targetShape != outputInfo.GetShape())
810 {
811 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
812 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100813 validateFunc(outputInfo, isSupported);
814 }
815 else
816 {
817 isSupported = AreDynamicTensorsSupported();
818 }
Kevin May42477c12020-03-26 13:34:14 +0000819
820 if (!isSupported)
821 {
822 return false;
823 }
824
825 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +0100826 if (!layer)
827 {
828 return Fail("%s: Could not add the ReshapeLayer", __func__);
829 }
Kevin May42477c12020-03-26 13:34:14 +0000830 input.Connect(layer->GetInputSlot(0));
831
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100832 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000833}
834
835template<typename HalPolicy,
Teresa Charlinf931af92020-04-10 16:46:53 +0100836 typename HalOperation = typename HalPolicy::Operation,
837 typename HalModel = typename HalPolicy::Model>
838bool ConvertGather(const HalOperation& operation, const HalModel& model, ConversionData& data)
839{
840 using HalOperand = typename HalPolicy::Operand;
841 using HalOperandType = typename HalPolicy::OperandType;
842
843 ALOGV("HalPolicy::ConvertGather()");
844
845 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
846 if (!input.IsValid())
847 {
848 return Fail("%s: Operation has invalid input", __func__);
849 }
850 auto inputDimensions = input.GetTensorInfo().GetNumDimensions();
851
852 LayerInputHandle indices = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data);
853 if (!indices.IsValid())
854 {
855 return Fail("%s: Operation has invalid indices", __func__);
856 }
857 auto indicesDimensions = indices.GetTensorInfo().GetNumDimensions();
858
859 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
860 if (!output)
861 {
862 return Fail("%s: Operation has invalid output", __func__);
863 }
864 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
865 auto outputDimensions = outputInfo.GetNumDimensions();
Teresa Charlinf931af92020-04-10 16:46:53 +0100866 if (outputDimensions != inputDimensions + indicesDimensions - 1)
867 {
868 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 +0100869 __func__, outputDimensions, inputDimensions, indicesDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100870 }
871
Finn Williamsf769f292021-06-25 12:53:09 +0100872 uint32_t axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100873 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
874 {
875 return Fail("%s: Operation has invalid or unsupported axis operand", __func__);
876 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100877 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
Teresa Charlinf931af92020-04-10 16:46:53 +0100878 {
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100879 return Fail("%s: Operation has invalid axis: %d. It is out of bounds [-%d, %d))", __func__, axis,
880 inputDimensions, inputDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100881 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100882
883 GatherDescriptor desc;
884 desc.m_Axis = axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100885
886 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100887 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
888 {
889 FORWARD_LAYER_SUPPORT_FUNC(__func__,
890 IsGatherSupported,
891 data.m_Backends,
892 isSupported,
893 input.GetTensorInfo(),
894 indices.GetTensorInfo(),
895 outputInfo,
896 desc);
897 };
898
899 if(!IsDynamicTensor(outputInfo))
900 {
901 validateFunc(outputInfo, isSupported);
902 }
903 else
904 {
905 isSupported = AreDynamicTensorsSupported();
906 }
907
Teresa Charlinf931af92020-04-10 16:46:53 +0100908 if (!isSupported)
909 {
910 return false;
911 }
912
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100913 IConnectableLayer* layer = data.m_Network->AddGatherLayer(desc);
Mike Kellye2d611e2021-10-14 12:35:58 +0100914 if (!layer)
915 {
916 return Fail("%s: Could not add the GatherLayer", __func__);
917 }
Teresa Charlinf931af92020-04-10 16:46:53 +0100918 input.Connect(layer->GetInputSlot(0));
919 indices.Connect(layer->GetInputSlot(1));
920
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100921 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100922}
923
924template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000925 typename HalOperation = typename HalPolicy::Operation,
926 typename HalModel = typename HalPolicy::Model>
927bool ConvertGroupedConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
928{
929 using HalOperand = typename HalPolicy::Operand;
930 using HalOperandType = typename HalPolicy::OperandType;
931
932 ALOGV("HalPolicy::ConvertGroupedConv2d()");
933
934 //
935 // Parse data
936 //
937 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
938 if (!input.IsValid())
939 {
940 return Fail("%s: Operation has invalid inputs", __func__);
941 }
942 const TensorInfo& inputInfo = input.GetTensorInfo();
943
944 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
945 if (!output)
946 {
947 return Fail("%s: Could not read output 0", __func__);
948 }
Finn Williamsb0331172020-10-08 14:33:13 +0100949 TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000950
951 // Look ahead to determine data layout
952 DataLayout dataLayout = DataLayout::NHWC;
953 if (operation.inputs.size() == 12)
954 {
955 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
956 }
957 else
958 {
959 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
960 }
961
962 // NOTE:
963 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
964 // but Arm NN expects the filter's height and width indices to match the input's height and
965 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
966 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
967 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
968 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
969 model, data, ohwiToOihw) :
970 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
971 const ConstTensorPin biasesPin =
972 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
973 if (!weightsPin.IsValid() || !biasesPin.IsValid())
974 {
975 return Fail("%s: Operation has invalid inputs", __func__);
976 }
977
978 ConstTensor weights = weightsPin.GetConstTensor();
979 ConstTensor biases = biasesPin.GetConstTensor();
980 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
981
982 const TensorShape& inputShape = inputInfo.GetShape();
983 const TensorShape& outputShape = outputInfo.GetShape();
984 const TensorShape& weightsShape = weights.GetShape();
Kevin May42477c12020-03-26 13:34:14 +0000985
986 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
987 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
988 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
989 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
990
991 Convolution2dDescriptor desc;
992 desc.m_DataLayout = dataLayout;
993 desc.m_BiasEnabled = true;
994
Finn Williamsf769f292021-06-25 12:53:09 +0100995 unsigned int numGroups;
Kevin May42477c12020-03-26 13:34:14 +0000996 ActivationFn activation;
997
998 if (operation.inputs.size() == 12)
999 {
1000 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1001 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1002 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1003 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1004 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1005 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1006 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
1007 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
1008 {
1009 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1010 }
1011
1012 }
1013 else if (operation.inputs.size() == 9)
1014 {
1015 android::nn::PaddingScheme paddingScheme;
1016 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1017 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1018 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1019 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
1020 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
1021 {
1022 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1023 }
1024
1025 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
1026 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
1027
1028 const uint32_t kernelX = weightsShape[widthIndex];
1029 const uint32_t kernelY = weightsShape[heightIndex];
1030
1031 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1032 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1033 }
1034 else
1035 {
1036 return Fail("%s: Unsupported number of operation inputs", __func__);
1037 }
1038
Finn Williamsb0331172020-10-08 14:33:13 +01001039 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1040 const unsigned int outputChannels = weightsShape[0];
Kevin May42477c12020-03-26 13:34:14 +00001041
1042 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
1043 const unsigned int channelMultiplier = outputChannels / numGroups;
1044
1045 //
1046 // Validate all relevant inputs
1047 //
1048 if (numGroups <= 0)
1049 {
1050 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
1051 }
1052
1053 if (outputChannels % numGroups != 0u)
1054 {
1055 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
1056 }
1057
1058 //
1059 // Set up Splitter layer
1060 //
1061 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
1062 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
1063
1064 TensorInfo splitterOutputInfo(4,
1065 splitterDimSizes,
1066 inputInfo.GetDataType(),
1067 inputInfo.GetQuantizationScale(),
1068 inputInfo.GetQuantizationOffset());
1069
1070 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
1071
1072 ViewsDescriptor splitterDesc(numGroups);
1073 for (unsigned int group = 0u; group < numGroups; ++group)
1074 {
1075 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
1076 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
1077 {
1078 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
1079 }
1080 }
1081
1082 bool isSupported = false;
1083 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1084 IsSplitterSupported,
1085 data.m_Backends,
1086 isSupported,
1087 inputInfo,
1088 splitterOutputInfos,
1089 splitterDesc);
1090 if (!isSupported)
1091 {
1092 return false;
1093 }
1094
1095 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
1096 if (!splitterLayer)
1097 {
1098 return Fail("%s: Failed to add SplitterLayer", __func__);
1099 }
1100
1101 input.Connect(splitterLayer->GetInputSlot(0));
1102 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
1103 {
1104 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
1105 }
1106
1107 //
1108 // Set up Convolution2d layers for each group
1109 //
1110
1111 // Set up group tensor shapes
1112 TensorShape groupInputShape(inputShape);
1113 groupInputShape[channelsIndex] = channelsPerGroup;
1114
Kevin May42477c12020-03-26 13:34:14 +00001115 TensorShape groupWeightsShape(weightsShape);
1116 groupWeightsShape[0] /= channelMultiplier * numGroups;
1117
1118 TensorShape groupBiasesShape({ 1 });
1119
1120 // Set up group tensor infos
1121 TensorInfo groupInputInfo(inputInfo);
1122 groupInputInfo.SetShape(groupInputShape);
1123
1124 const TensorInfo& weightsInfo = weights.GetInfo();
1125 TensorInfo groupWeightsInfo(weightsInfo);
1126 groupWeightsInfo.SetShape(groupWeightsShape);
1127
1128 const TensorInfo& biasesInfo = biases.GetInfo();
1129 TensorInfo groupBiasesInfo(biasesInfo);
1130 groupBiasesInfo.SetShape(groupBiasesShape);
1131
1132 TensorInfo groupOutputInfo(outputInfo);
Finn Williamsb0331172020-10-08 14:33:13 +01001133
1134 TensorShape groupOutputShape(outputShape);
1135 const bool isDynamic = IsDynamicTensor(outputInfo);
1136 if (!isDynamic)
1137 {
1138 groupOutputShape[channelsIndex] = 1;
1139 }
Kevin May42477c12020-03-26 13:34:14 +00001140 groupOutputInfo.SetShape(groupOutputShape);
1141
1142 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1143 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1144
1145 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1146 for (unsigned int group = 0u; group < numGroups; ++group)
1147 {
1148 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1149 {
1150 auto index = group * channelMultiplier + m;
1151
1152 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1153 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1154
1155 if (weightsInfo.HasPerAxisQuantization())
1156 {
1157 // Extract per-axis quantization scales for group weights
1158 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1159 groupWeightsInfo.SetQuantizationScales(
1160 std::vector<float>(weightsQuantScales.begin() + index,
1161 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1162
1163 // Extract per-axis quantization scales for group biases
1164 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1165 groupBiasesInfo.SetQuantizationScales(
1166 std::vector<float>(biasesQuantScales.begin() + index,
1167 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1168 }
1169
1170 // Extract weights and biases data for current group convolution
1171 ConstTensor groupWeights(groupWeightsInfo,
1172 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1173 weightsDataOffset));
1174 ConstTensor groupBiases(groupBiasesInfo,
1175 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1176 biasesDataOffset));
1177
1178 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001179 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1180 {
1181 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1182 IsConvolution2dSupported,
1183 data.m_Backends,
1184 isSupported,
1185 groupInputInfo,
1186 outputInfo,
1187 desc,
1188 groupWeightsInfo,
1189 Optional<TensorInfo>(groupBiasesInfo));
1190 };
1191
1192 if(!isDynamic)
1193 {
1194 validateFunc(groupOutputInfo, isSupported);
1195 }
1196 else
1197 {
1198 isSupported = AreDynamicTensorsSupported();
1199 }
1200
Kevin May42477c12020-03-26 13:34:14 +00001201 if (!isSupported)
1202 {
1203 return false;
1204 }
1205
1206 IConnectableLayer* convLayer =
1207 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1208 if (!convLayer)
1209 {
1210 return Fail("%s: AddConvolution2dLayer failed", __func__);
1211 }
1212
1213 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1214 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1215
Finn Williamsb0331172020-10-08 14:33:13 +01001216 if(isDynamic)
1217 {
1218 convLayer->GetOutputSlot(0).IsTensorInfoSet();
1219
1220 validateFunc(convLayer->GetOutputSlot(0).GetTensorInfo(), isSupported);
1221
1222 outputInfo = convLayer->GetOutputSlot(0).GetTensorInfo();
1223
1224 if (!isSupported)
1225 {
1226 return false;
1227 }
1228 }
1229
Kevin May42477c12020-03-26 13:34:14 +00001230 convLayers[index] = convLayer;
1231 }
1232 }
1233
1234 //
1235 // Set up Concat layer
1236 //
Finn Williamsb0331172020-10-08 14:33:13 +01001237 ConcatDescriptor concatDescriptor;
1238 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1239 concatDescriptor = ConcatDescriptor(weightsShape[0]);
Kevin May42477c12020-03-26 13:34:14 +00001240 for (unsigned int group = 0u; group < numGroups; ++group)
1241 {
1242 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1243 {
1244 auto index = group * channelMultiplier + m;
1245 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1246 concatDescriptor.SetConcatAxis(channelsIndex);
1247 }
1248 }
1249
1250 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001251 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1252 IsConcatSupported,
1253 data.m_Backends,
1254 isSupported,
1255 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1256 outputInfo,
1257 concatDescriptor);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001258
Kevin May42477c12020-03-26 13:34:14 +00001259 if (!isSupported)
1260 {
1261 return false;
1262 }
1263
1264 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1265 if (!concatLayer)
1266 {
1267 return Fail("%s: AddConcatLayer failed", __func__);
1268 }
1269
1270 for (unsigned int group = 0u; group < numGroups; ++group)
1271 {
1272 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1273 {
1274 auto index = group * channelMultiplier + m;
1275 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1276 }
1277 }
1278 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1279
Kevin Mayfcf2a152020-09-08 16:06:32 +01001280 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
Finn Williamsb0331172020-10-08 14:33:13 +01001281 data, nullptr, nullptr, activation);
Kevin May42477c12020-03-26 13:34:14 +00001282}
1283
1284template<typename HalPolicy,
1285 typename HalOperation = typename HalPolicy::Operation,
1286 typename HalModel = typename HalPolicy::Model>
1287bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1288{
1289 using HalOperand = typename HalPolicy::Operand;
1290 using HalOperandType = typename HalPolicy::OperandType;
1291
1292 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1293
1294 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1295 if (!input.IsValid())
1296 {
1297 return Fail("%s: Operation has an invalid input 0", __func__);
1298 }
1299
1300 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1301 if (!output)
1302 {
1303 return Fail("%s: Operation has an invalid output", __func__);
1304 }
1305
1306 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001307
1308 // Determine data type of input tensor
1309 HalOperandType inputType;
1310 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1311 {
1312 return Fail("%s: Operation has invalid inputs", __func__);
1313 }
1314
1315 InstanceNormalizationDescriptor desc;
1316
1317 // Read gamma, beta & epsilon
1318 if (inputType == HalOperandType::TENSOR_FLOAT16)
1319 {
1320 Half fp16Gamma;
1321 Half fp16Beta;
1322 Half fp16Epsilon;
1323
1324 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1325 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1326 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1327 {
1328 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1329 }
1330
1331 desc.m_Gamma = static_cast<float>(fp16Gamma);
1332 desc.m_Beta = static_cast<float>(fp16Beta);
1333 desc.m_Eps = static_cast<float>(fp16Epsilon);
1334 }
1335 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1336 {
1337 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1338 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1339 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1340 {
1341 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1342 }
1343 }
1344 else
1345 {
1346 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1347 }
1348
1349 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1350
1351 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001352 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1353 {
1354 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1355 IsInstanceNormalizationSupported,
1356 data.m_Backends,
1357 isSupported,
1358 input.GetTensorInfo(),
1359 outputInfo,
1360 desc);
1361 };
1362
1363 if(IsDynamicTensor(outputInfo))
1364 {
1365 isSupported = AreDynamicTensorsSupported();
1366 }
1367 else
1368 {
1369 validateFunc(outputInfo, isSupported);
1370 }
1371
Kevin May42477c12020-03-26 13:34:14 +00001372 if (!isSupported)
1373 {
1374 return false;
1375 }
1376
1377 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1378 input.Connect(layer->GetInputSlot(0));
1379
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001380 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001381}
1382
1383template<typename HalPolicy,
1384 typename HalOperation = typename HalPolicy::Operation,
1385 typename HalModel = typename HalPolicy::Model>
1386bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1387{
1388 using HalOperand = typename HalPolicy::Operand;
1389 using HalOperandType = typename HalPolicy::OperandType;
1390
1391 ALOGV("HalPolicy::ConvertLogSoftmax()");
1392
1393 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1394 if (!input.IsValid())
1395 {
1396 return Fail("%s: Failed to read input 0", __func__);
1397 }
1398
1399 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1400 if (!output)
1401 {
1402 return Fail("%s: Failed to read output", __func__);
1403 }
1404
1405 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001406
1407 // Determine data type of input tensor
1408 HalOperandType inputType;
1409 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1410 {
1411 return Fail("%s: Operation has invalid inputs", __func__);
1412 }
1413
1414 LogSoftmaxDescriptor descriptor;
1415
1416 // Read beta
1417 if (inputType == HalOperandType::TENSOR_FLOAT16)
1418 {
1419 Half fp16Beta;
1420 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1421 {
1422 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1423 }
1424
1425 descriptor.m_Beta = static_cast<float>(fp16Beta);
1426 }
1427 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1428 {
1429 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1430 {
1431 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1432 }
1433 }
1434 else
1435 {
1436 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1437 }
1438
1439 // Read axis
1440 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1441 {
1442 return Fail("%s: Failed to read input 2", __func__);
1443 }
1444
1445 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001446 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1447 {
1448 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1449 IsLogSoftmaxSupported,
1450 data.m_Backends,
1451 isSupported,
1452 input.GetTensorInfo(),
1453 outputInfo,
1454 descriptor);
1455 };
1456
1457 if(IsDynamicTensor(outputInfo))
1458 {
1459 isSupported = AreDynamicTensorsSupported();
1460 }
1461 else
1462 {
1463 validateFunc(outputInfo, isSupported);
1464 }
1465
Kevin May42477c12020-03-26 13:34:14 +00001466 if (!isSupported)
1467 {
1468 return false;
1469 }
1470
1471 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
1472 if (!layer)
1473 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001474 return Fail("%s: Could not add the LogSoftmaxLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001475 }
Kevin May42477c12020-03-26 13:34:14 +00001476 input.Connect(layer->GetInputSlot(0));
1477
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001478 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001479}
1480
1481template<typename HalPolicy,
1482 typename HalOperation = typename HalPolicy::Operation,
1483 typename HalModel = typename HalPolicy::Model>
1484bool ConvertMaximum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1485{
1486 using HalOperand = typename HalPolicy::Operand;
1487
1488 ALOGV("HalPolicy::ConvertMaximum()");
1489
1490 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1491 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1492
1493 if (!input0.IsValid() || !input1.IsValid())
1494 {
1495 return Fail("%s: Operation has invalid inputs", __func__);
1496 }
1497
1498 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1499 if (!outputOperand)
1500 {
1501 return Fail("%s: Could not read output", __func__);
1502 }
1503
1504 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001505
1506 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001507 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
1508 {
1509 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1510 IsMaximumSupported,
1511 data.m_Backends,
1512 isSupported,
1513 input0.GetTensorInfo(),
1514 input1.GetTensorInfo(),
1515 outInfo);
1516 };
1517
1518 if(IsDynamicTensor(outInfo))
1519 {
1520 isSupported = AreDynamicTensorsSupported();
1521 }
1522 else
1523 {
1524 validateFunc(outInfo, isSupported);
1525 }
Kevin May42477c12020-03-26 13:34:14 +00001526
1527 if (!isSupported)
1528 {
1529 return false;
1530 }
1531
1532 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +01001533 if (!layer)
1534 {
1535 return Fail("%s: Could not add the MaximumLayer", __func__);
1536 }
Kevin May42477c12020-03-26 13:34:14 +00001537 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1538 if (!isReshapeSupported)
1539 {
1540 return false;
1541 }
1542
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001543 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001544}
1545
1546template<typename HalPolicy,
1547 typename HalOperation = typename HalPolicy::Operation,
1548 typename HalModel = typename HalPolicy::Model>
1549bool ConvertMinimum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1550{
1551 using HalOperand = typename HalPolicy::Operand;
1552
1553 ALOGV("HalPolicy::ConvertMinimum()");
1554
1555 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1556 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1557
1558 if (!input0.IsValid() || !input1.IsValid())
1559 {
1560 return Fail("%s: Operation has invalid inputs", __func__);
1561 }
1562
1563 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1564 if (!output)
1565 {
1566 return Fail("%s: Could not read output 0", __func__);
1567 }
1568
1569 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001570
1571 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001572 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1573 {
1574 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1575 IsMinimumSupported,
1576 data.m_Backends,
1577 isSupported,
1578 input0.GetTensorInfo(),
1579 input1.GetTensorInfo(),
1580 outputInfo);
1581 };
1582
1583 if(IsDynamicTensor(outputInfo))
1584 {
1585 isSupported = AreDynamicTensorsSupported();
1586 }
1587 else
1588 {
1589 validateFunc(outputInfo, isSupported);
1590 }
Kevin May42477c12020-03-26 13:34:14 +00001591
1592 if (!isSupported)
1593 {
1594 return false;
1595 }
1596
1597 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +01001598 if (!layer)
1599 {
1600 return Fail("%s: Could not add the MinimumLayer", __func__);
1601 }
Kevin May42477c12020-03-26 13:34:14 +00001602 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1603 if (!isReshapeSupported)
1604 {
1605 return false;
1606 }
1607
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001608 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001609}
1610
1611template<typename HalPolicy,
1612 typename HalOperation = typename HalPolicy::Operation,
1613 typename HalModel = typename HalPolicy::Model>
1614bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1615{
1616 using HalOperand = typename HalPolicy::Operand;
1617 using HalOperandType = typename HalPolicy::OperandType;
1618
1619 ALOGV("HalPolicy::ConvertPadV2()");
1620
1621 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1622 if (!input.IsValid())
1623 {
1624 return Fail("%s: Could not read input 0", __func__);
1625 }
1626
1627 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1628 if (!output)
1629 {
1630 return Fail("%s: Could not read output", __func__);
1631 }
1632
1633 const TensorInfo& inputInfo = input.GetTensorInfo();
1634 unsigned int rank = inputInfo.GetNumDimensions();
1635
1636 PadDescriptor descriptor;
1637 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1638 {
1639 return Fail("%s: Could not convert paddings", __func__);
1640 }
1641
1642 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001643
1644 // Determine type of padding value
1645 HalOperandType operandType0;
1646 HalOperandType operandType2;
1647
1648 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1649 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1650 {
1651 return Fail("%s: Operation has invalid inputs", __func__);
1652 }
1653
1654 // Read value to use for padding
1655 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1656 {
1657 Half f16PadValue;
1658 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1659 {
1660 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1661 }
1662
1663 descriptor.m_PadValue = f16PadValue;
1664 }
1665 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1666 {
1667 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1668 {
1669 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1670 }
1671 }
Teresa Charlind3381d52021-06-02 18:35:16 +01001672 else if (isQuantizedOperand(operandType0) && operandType2 == HalOperandType::INT32)
Kevin May42477c12020-03-26 13:34:14 +00001673 {
1674 int32_t intPadValue = 0;
1675 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1676 {
1677 return Fail("%s: Could not read input 2 (INT32)", __func__);
1678 }
1679 descriptor.m_PadValue = intPadValue;
1680 }
1681 else
1682 {
1683 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1684 }
1685
1686 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001687 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1688 {
1689 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1690 IsPadSupported,
1691 data.m_Backends,
1692 isSupported,
1693 inputInfo,
1694 outputInfo,
1695 descriptor);
1696 };
1697
1698 if(IsDynamicTensor(outputInfo))
1699 {
1700 isSupported = AreDynamicTensorsSupported();
1701 }
1702 else
1703 {
1704 validateFunc(outputInfo, isSupported);
1705 }
1706
Kevin May42477c12020-03-26 13:34:14 +00001707 if (!isSupported)
1708 {
1709 return false;
1710 }
1711
1712 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +01001713 if (!layer)
1714 {
1715 return Fail("%s: Could not add the PadLayer", __func__);
1716 }
Kevin May42477c12020-03-26 13:34:14 +00001717 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001718
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001719 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001720}
1721
1722template<typename HalPolicy,
1723 typename HalOperation = typename HalPolicy::Operation,
1724 typename HalModel = typename HalPolicy::Model>
1725bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1726{
1727 using HalOperand = typename HalPolicy::Operand;
1728
1729 ALOGV("HalPolicy::ConvertPrelu()");
1730
1731 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1732 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1733
1734 if (!input.IsValid() || !alpha.IsValid())
1735 {
1736 return Fail("%s: Operation has invalid inputs", __func__);
1737 }
1738
1739 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1740
1741 if (!output)
1742 {
1743 return Fail("%s: Could not read output", __func__);
1744 }
1745
1746 const TensorInfo& inputInfo = input.GetTensorInfo();
1747 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1748 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1749
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001750 bool isSupported = false;
1751 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001752 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001753 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1754 IsPreluSupported,
1755 data.m_Backends,
1756 isSupported,
1757 inputInfo,
1758 alphaInfo,
1759 outputInfo);
1760 };
1761
1762 if(IsDynamicTensor(outputInfo))
1763 {
1764 isSupported = AreDynamicTensorsSupported();
1765 }
1766 else
1767 {
1768 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001769 }
1770
Kevin May42477c12020-03-26 13:34:14 +00001771 if (!isSupported)
1772 {
1773 return false;
1774 }
1775
1776 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Kevin May42477c12020-03-26 13:34:14 +00001777 if (!layer)
1778 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001779 return Fail("%s: Could not add the PreluLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001780 }
1781
1782 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1783 if (!isReshapeSupported)
1784 {
1785 return false;
1786 }
1787
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001788 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001789}
1790
1791template<typename HalPolicy,
1792 typename HalOperation = typename HalPolicy::Operation,
1793 typename HalModel = typename HalPolicy::Model>
1794bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1795{
1796 using HalOperand = typename HalPolicy::Operand;
1797
1798 ALOGV("HalPolicy::ConvertQuantize()");
1799
1800 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1801 if (!input.IsValid())
1802 {
1803 return Fail("%s: Operation has invalid input", __func__);
1804 }
1805
1806 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1807 if (!outputOperand)
1808 {
1809 return Fail("%s: Operation has invalid outputs", __func__);
1810 }
1811
1812 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001813
1814 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001815 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1816 {
1817 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1818 IsQuantizeSupported,
1819 data.m_Backends,
1820 isSupported,
1821 input.GetTensorInfo(),
1822 outputInfo);
1823 };
1824
1825 if(IsDynamicTensor(outputInfo))
1826 {
1827 isSupported = AreDynamicTensorsSupported();
1828 }
1829 else
1830 {
1831 validateFunc(outputInfo, isSupported);
1832 }
1833
Kevin May42477c12020-03-26 13:34:14 +00001834 if (!isSupported)
1835 {
1836 return false;
1837 }
1838
1839 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +01001840 if (!layer)
1841 {
1842 return Fail("%s: Could not add the QuantizeLayer", __func__);
1843 }
Kevin May42477c12020-03-26 13:34:14 +00001844 input.Connect(layer->GetInputSlot(0));
1845
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001846 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001847}
1848
1849template<typename HalPolicy,
1850 typename HalOperation = typename HalPolicy::Operation,
1851 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001852bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001853{
1854 using HalOperand = typename HalPolicy::Operand;
1855
Sadik Armagan813f2302020-05-19 14:10:30 +01001856 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001857
1858 //Inputs:
1859 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1860 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1861 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1862 if (!input.IsValid())
1863 {
1864 return Fail("%s: Could not read input 0: input", __func__);
1865 }
1866
1867 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1868 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1869 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1870 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1871 if (!previousCellStateIn.IsValid())
1872 {
1873 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1874 }
1875
1876 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1877 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1878 // is quantized with a fixed quantization range of -1, 127/128.
1879 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1880 if (!previousOutputIn.IsValid())
1881 {
1882 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1883 }
1884
1885 // Get the input tensors:
1886 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1887 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1888 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1889 const ConstTensorPin inputToInputWeightsPin =
1890 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1891
1892 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1893 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1894 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1895 const ConstTensorPin inputToForgetWeightsPin =
1896 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1897
1898 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1899 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1900 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1901 const ConstTensorPin inputToCellWeightsPin =
1902 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1903
1904 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1905 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1906 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1907 const ConstTensorPin inputToOutputWeightsPin =
1908 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1909
1910 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1911 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1912 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1913 const ConstTensorPin recurrentToInputWeightsPin =
1914 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1915
1916 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1917 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1918 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1919 const ConstTensorPin recurrentToForgetWeightsPin =
1920 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1921
1922 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1923 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1924 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1925 const ConstTensorPin recurrentToCellWeightsPin =
1926 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1927
1928 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1929 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1930 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1931 const ConstTensorPin recurrentToOutputWeightsPin =
1932 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1933
1934 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1935 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1936 // of input and weights scales and zeroPoint equal to 0.
1937 const ConstTensorPin inputGateBiasPin =
1938 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1939
1940 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1941 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1942 // of input and weights scales and zeroPoint equal to 0.
1943 const ConstTensorPin forgetGateBiasPin =
1944 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1945
1946 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1947 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1948 // and weights scales and zeroPoint equal to 0.
1949 const ConstTensorPin cellBiasPin =
1950 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1951
1952 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1953 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1954 // of input and weights scales and zeroPoint equal to 0.
1955 const ConstTensorPin outputGateBiasPin =
1956 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1957
1958 if (!inputToInputWeightsPin.IsValid() ||
1959 !inputToForgetWeightsPin.IsValid() ||
1960 !inputToCellWeightsPin.IsValid() ||
1961 !inputToOutputWeightsPin.IsValid() ||
1962 !recurrentToInputWeightsPin.IsValid() ||
1963 !recurrentToForgetWeightsPin.IsValid() ||
1964 !recurrentToCellWeightsPin.IsValid() ||
1965 !recurrentToOutputWeightsPin.IsValid() ||
1966 !inputGateBiasPin.IsValid() ||
1967 !forgetGateBiasPin.IsValid() ||
1968 !cellBiasPin.IsValid() ||
1969 !outputGateBiasPin.IsValid())
1970 {
1971 return Fail("%s: Operation has invalid tensor inputs", __func__);
1972 }
1973
1974 // Outputs:
1975 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1976 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1977 // of -2^4, 2^4 * 32767/32768.
1978 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1979 if (!cellStateOut)
1980 {
1981 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1982 }
1983
1984 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1985 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1986 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1987 if (!output)
1988 {
1989 return Fail("%s: Could not read output 1: output", __func__);
1990 }
1991
1992 // Inputs
1993 const TensorInfo& inputInfo = input.GetTensorInfo();
1994 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1995 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1996
1997 // Outputs
1998 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1999 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2000
2001 // Dynamic tensors currently not supported
2002 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
2003 {
2004 return Fail("%s: Dynamic output tensors are not supported", __func__);
2005 }
2006
2007 QuantizedLstmInputParams params;
2008
2009 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2010 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2011 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2012 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2013 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2014 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2015 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2016 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2017 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2018 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2019 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2020 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2021
2022 QuantizedLstmInputParamsInfo paramsInfo;
2023 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2024 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2025 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2026 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2027 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2028 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2029 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2030 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2031 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2032 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2033 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2034 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2035
2036 bool isSupported = false;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002037 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2038 {
2039 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2040 IsQuantizedLstmSupported,
2041 data.m_Backends,
2042 isSupported,
2043 inputInfo,
2044 previousCellStateInInfo,
2045 previousOutputInInfo,
2046 cellStateOutInfo,
2047 outputInfo,
2048 paramsInfo);
2049 };
2050
2051 bool isDynamic = false;
2052 if (!IsDynamicTensor(cellStateOutInfo) &&
2053 !IsDynamicTensor(outputInfo))
2054 {
2055 validateFunc(outputInfo, isSupported);
2056 }
2057 else
2058 {
2059 isDynamic = true;
2060 isSupported = AreDynamicTensorsSupported();
2061 }
Kevin May42477c12020-03-26 13:34:14 +00002062
2063 if (!isSupported)
2064 {
2065 return false;
2066 }
2067
2068 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
2069 input.Connect(layer->GetInputSlot(0));
2070 previousCellStateIn.Connect(layer->GetInputSlot(1));
2071 previousOutputIn.Connect(layer->GetInputSlot(2));
2072
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002073 if (!isDynamic)
2074 {
2075 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2076 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
2077 }
2078 else
2079 {
2080 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2081 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002082 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002083 }
2084
Kevin May42477c12020-03-26 13:34:14 +00002085}
2086
2087template<typename HalPolicy,
2088 typename HalOperation = typename HalPolicy::Operation,
2089 typename HalModel = typename HalPolicy::Model>
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002090bool ConvertReduce(const HalOperation& operation,
2091 const HalModel& model,
2092 ConversionData& data,
2093 ReduceOperation reduceOperation)
2094{
2095 using HalOperand = typename HalPolicy::Operand;
2096 using HalOperandType = typename HalPolicy::OperandType;
2097
2098 armnn::ReduceDescriptor descriptor;
2099 descriptor.m_ReduceOperation = reduceOperation;
2100
2101 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2102 if (!input.IsValid())
2103 {
2104 return Fail("%s: Operation has invalid inputs", __func__);
2105 }
2106 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2107
2108 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2109 if (!output)
2110 {
2111 return Fail("%s: Could not read output 0", __func__);
2112 }
2113 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2114
2115 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2116 if (!axisOperand)
2117 {
2118 return Fail("%s: Could not read input 1", __func__);
2119 }
2120 std::vector<int32_t> axis;
2121 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2122 {
2123 return Fail("%s: Input 1 has invalid values", __func__);
2124 }
2125
2126 // Convert the axis to unsigned int and remove duplicates.
2127 unsigned int rank = inputInfo.GetNumDimensions();
2128 std::set<unsigned int> uniqueAxis;
2129 std::transform(axis.begin(), axis.end(),
2130 std::inserter(uniqueAxis, uniqueAxis.begin()),
2131 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2132 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
2133
2134 // Get the "keep dims" flag.
2135 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::BOOL, descriptor.m_KeepDims, model, data))
2136 {
2137 return Fail("%s: Could not read input 2", __func__);
2138 }
2139
2140 bool isSupported = false;
2141 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2142 {
2143 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2144 IsReduceSupported,
2145 data.m_Backends,
2146 isSupported,
2147 inputInfo,
2148 outputInfo,
2149 descriptor);
2150 };
2151
2152 if(!IsDynamicTensor(outputInfo))
2153 {
2154 validateFunc(outputInfo, isSupported);
2155 }
2156 else
2157 {
2158 isSupported = AreDynamicTensorsSupported();
2159 }
2160
2161 if (!isSupported)
2162 {
2163 return false;
2164 }
2165
2166 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +01002167 if (!layer)
2168 {
2169 return Fail("%s: Could not add the ReduceLayer", __func__);
2170 }
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002171 input.Connect(layer->GetInputSlot(0));
2172
2173 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
2174}
2175
2176template<typename HalPolicy,
2177 typename HalOperation = typename HalPolicy::Operation,
2178 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002179bool ConvertResize(const HalOperation& operation,
2180 const HalModel& model,
2181 ConversionData& data,
2182 ResizeMethod resizeMethod)
2183{
2184 using HalOperand = typename HalPolicy::Operand;
2185 using HalOperandType = typename HalPolicy::OperandType;
2186 ALOGV("HalPolicy::ConvertResize()");
2187 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
2188
2189 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2190 if (!input.IsValid())
2191 {
2192 return Fail("%s: Could not read input 0", __func__);
2193 }
2194
2195 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2196 if (!output)
2197 {
2198 return Fail("%s: Could not read output 0", __func__);
2199 }
2200
2201 const TensorInfo& inputInfo = input.GetTensorInfo();
2202 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2203
Kevin May42477c12020-03-26 13:34:14 +00002204 ResizeDescriptor descriptor;
2205 descriptor.m_Method = resizeMethod;
2206 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
2207
2208 HalOperandType operandType1;
2209 HalOperandType operandType2;
2210
2211 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
2212 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
2213 {
2214 return Fail("%s: Operation has invalid inputs", __func__);
2215 }
2216
2217 if (operandType1 != operandType2)
2218 {
2219 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
2220 }
2221
2222 if (operandType1 == HalOperandType::INT32)
2223 {
2224 // Case 1: resizing by shape
2225 int32_t targetWidth = 0;
2226 int32_t targetHeight = 0;
2227
2228 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
2229 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
2230 {
2231 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
2232 }
2233
2234 if (targetWidth < 0 || targetHeight < 0)
2235 {
2236 return Fail("%s: Operation has invalid inputs for resizing by shape. "
2237 "Target width/height cannot be < 0", __func__);
2238 }
2239
2240 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
2241 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
2242 }
2243 else if (operandType1 == HalOperandType::FLOAT32)
2244 {
2245 // Case 2: resizing by scale
2246 float widthScale = 1.0f;
2247 float heightScale = 1.0f;
2248
2249 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2250 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2251 {
2252 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2253 }
2254
2255 const TensorShape& inputShape = inputInfo.GetShape();
2256 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2257
2258 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2259 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2260
2261 descriptor.m_TargetWidth = std::floor(width * widthScale);
2262 descriptor.m_TargetHeight = std::floor(height * heightScale);
2263 }
2264 else if (operandType1 == HalOperandType::FLOAT16)
2265 {
2266 Half widthScale;
2267 Half heightScale;
2268
2269 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2270 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2271 {
2272 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2273 }
2274
2275 const TensorShape& inputShape = inputInfo.GetShape();
2276 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2277
2278 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2279 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2280
2281 descriptor.m_TargetWidth = std::floor(width * widthScale);
2282 descriptor.m_TargetHeight = std::floor(height * heightScale);
2283 }
2284 else
2285 {
2286 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2287 }
2288
David Monahanf057e6f2020-06-22 09:55:23 +01002289 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2290 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002291
Kevin May42477c12020-03-26 13:34:14 +00002292 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002293 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2294 {
2295 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2296 IsResizeSupported,
2297 data.m_Backends,
2298 isSupported,
2299 inputInfo,
2300 outputInfo,
2301 descriptor);
2302 };
2303
2304 if(IsDynamicTensor(outputInfo))
2305 {
2306 isSupported = AreDynamicTensorsSupported();
2307 }
2308 else
2309 {
2310 validateFunc(outputInfo, isSupported);
2311 }
Kevin May42477c12020-03-26 13:34:14 +00002312
2313 if (!isSupported)
2314 {
2315 return false;
2316 }
2317
2318 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +01002319 if (!layer)
2320 {
2321 return Fail("%s: Could not add the ResizeLayer", __func__);
2322 }
Kevin May42477c12020-03-26 13:34:14 +00002323 input.Connect(layer->GetInputSlot(0));
2324
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002325 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002326}
2327
2328template<typename HalPolicy,
2329 typename HalOperation = typename HalPolicy::Operation,
2330 typename HalModel = typename HalPolicy::Model>
2331bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2332{
2333 using HalOperand = typename HalPolicy::Operand;
2334 using HalOperandType = typename HalPolicy::OperandType;
2335
2336 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2337
2338 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2339 if (!input.IsValid() )
2340 {
2341 return Fail("%s: Operation has invalid inputs", __func__);
2342 }
2343
2344 const TensorInfo& inputInfo = input.GetTensorInfo();
2345 unsigned int rank = inputInfo.GetNumDimensions();
2346 if (rank != 4)
2347 {
2348 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2349 }
2350
2351 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2352 if (!output)
2353 {
2354 return Fail("%s: Could not read output 0", __func__);
2355 }
2356
2357 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002358
2359 SpaceToDepthDescriptor desc;
2360
2361 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2362
2363 if (desc.m_BlockSize <= 1)
2364 {
2365 return Fail("%s: Block size must be at least 1 in all dimensions");
2366 }
2367
2368 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2369
2370 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002371 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2372 {
2373 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2374 IsSpaceToDepthSupported,
2375 data.m_Backends,
2376 isSupported,
2377 inputInfo,
2378 outputInfo,
2379 desc);
2380 };
2381
2382 if(IsDynamicTensor(outputInfo))
2383 {
2384 isSupported = AreDynamicTensorsSupported();
2385 }
2386 else
2387 {
2388 validateFunc(outputInfo, isSupported);
2389 }
2390
Kevin May42477c12020-03-26 13:34:14 +00002391 if (!isSupported)
2392 {
2393 return false;
2394 }
2395
2396 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Mike Kellye2d611e2021-10-14 12:35:58 +01002397 if (!layer)
2398 {
Mike Kelly1b46d132021-11-03 11:12:45 +00002399 return Fail("%s: Could not add the SpaceToDepthLayer", __func__);
Mike Kellye2d611e2021-10-14 12:35:58 +01002400 }
Kevin May42477c12020-03-26 13:34:14 +00002401 input.Connect(layer->GetInputSlot(0));
2402
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002403 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002404}
2405
2406template<typename HalPolicy,
2407 typename HalOperation = typename HalPolicy::Operation,
2408 typename HalModel = typename HalPolicy::Model>
2409bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2410{
2411 using HalOperand = typename HalPolicy::Operand;
2412 using HalOperandType = typename HalPolicy::OperandType;
2413
2414 ALOGV("HalPolicy::ConvertSoftmax()");
2415
2416 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2417 if (!input.IsValid())
2418 {
2419 return Fail("%s: Operation has invalid inputs", __func__);
2420 }
2421
2422 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2423 if (!outputOperand)
2424 {
2425 return Fail("%s: Operation has no outputs", __func__);
2426 }
2427
2428 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002429
2430 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002431 HalOperandType outputType = outputOperand->type;
2432
2433 // Read beta value
2434 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002435 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002436 Half value;
2437
2438 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2439 {
2440 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2441 }
2442
2443 desc.m_Beta = static_cast<float>(value);
2444 }
2445 else
2446 {
2447 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2448 {
2449 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2450 }
Kevin May42477c12020-03-26 13:34:14 +00002451 }
2452
2453 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002454 2,
2455 HalOperandType::INT32,
2456 desc.m_Axis,
2457 model,
2458 data))
Kevin May42477c12020-03-26 13:34:14 +00002459 {
2460 return Fail("%s: Operation has invalid inputs", __func__);
2461 }
2462
Kevin May42477c12020-03-26 13:34:14 +00002463 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002464 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2465 {
2466 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2467 IsSoftmaxSupported,
2468 data.m_Backends,
2469 isSupported,
2470 input.GetTensorInfo(),
2471 outputInfo,
2472 desc);
2473 };
2474
2475 if(IsDynamicTensor(outputInfo))
2476 {
2477 isSupported = AreDynamicTensorsSupported();
2478 }
2479 else
2480 {
2481 validateFunc(outputInfo, isSupported);
2482 }
2483
Kevin May42477c12020-03-26 13:34:14 +00002484 if (!isSupported)
2485 {
2486 return false;
2487 }
2488
2489 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Mike Kellye2d611e2021-10-14 12:35:58 +01002490 if (!layer)
2491 {
2492 return Fail("%s: Could not add the SoftmaxLayer", __func__);
2493 }
Kevin May42477c12020-03-26 13:34:14 +00002494 input.Connect(layer->GetInputSlot(0));
2495
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002496 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002497}
2498
2499template<typename HalPolicy,
2500 typename HalOperation = typename HalPolicy::Operation,
2501 typename HalModel = typename HalPolicy::Model>
2502bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2503{
2504 using HalOperand = typename HalPolicy::Operand;
2505 using HalOperandType = typename HalPolicy::OperandType;
2506
2507 ALOGV("HalPolicy::ConvertLstm()");
2508
2509 // Inputs:
2510 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2511 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2512 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2513 if (!input.IsValid())
2514 {
2515 return Fail("%s: Could not read input 0: input", __func__);
2516 }
2517 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2518 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2519 if (!outputStateIn.IsValid())
2520 {
2521 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2522 }
2523 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2524 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2525 if (!cellStateIn.IsValid())
2526 {
2527 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2528 }
2529
2530 // Get the mandatory input tensors:
2531 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2532 // [num_units, input_size].
2533 const ConstTensorPin inputToForgetWeightsPin =
2534 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2535 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2536 // [num_units, input_size].
2537 const ConstTensorPin inputToCellWeightsPin =
2538 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2539 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2540 // [num_units, input_size].
2541 const ConstTensorPin inputToOutputWeightsPin =
2542 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2543 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2544 // [num_units, output_size].
2545 const ConstTensorPin recurrentToForgetWeightsPin =
2546 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2547 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2548 // [num_units, output_size].
2549 const ConstTensorPin recurrentToCellWeightsPin =
2550 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2551 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2552 // [num_units, output_size].
2553 const ConstTensorPin recurrentToOutputWeightsPin =
2554 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2555 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2556 const ConstTensorPin forgetGateBiasPin =
2557 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2558 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2559 const ConstTensorPin cellBiasPin =
2560 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2561 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2562 const ConstTensorPin outputGateBiasPin =
2563 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2564
2565 if (!inputToForgetWeightsPin.IsValid() ||
2566 !inputToCellWeightsPin.IsValid() ||
2567 !inputToOutputWeightsPin.IsValid() ||
2568 !recurrentToForgetWeightsPin.IsValid() ||
2569 !recurrentToCellWeightsPin.IsValid() ||
2570 !recurrentToOutputWeightsPin.IsValid() ||
2571 !forgetGateBiasPin.IsValid() ||
2572 !cellBiasPin.IsValid() ||
2573 !outputGateBiasPin.IsValid())
2574 {
2575 return Fail("%s: Operation has invalid tensor inputs", __func__);
2576 }
2577
2578 // Get the optional input tensors:
2579 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2580 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2581 const ConstTensorPin inputToInputWeightsPin =
2582 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2583 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2584 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2585 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2586 const ConstTensorPin recurrentToInputWeightsPin =
2587 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2588 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2589 const ConstTensorPin cellToInputWeightsPin =
2590 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2591 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2592 const ConstTensorPin cellToForgetWeightsPin =
2593 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2594 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2595 const ConstTensorPin cellToOutputWeightsPin =
2596 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2597 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2598 const ConstTensorPin inputGateBiasPin =
2599 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2600 12,
2601 model,
2602 data,
2603 g_DontPermute,
2604 nullptr,
2605 true);
2606
2607 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2608 // [output_size, num_units].
2609 const ConstTensorPin projectionWeightsPin =
2610 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2611 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2612 const ConstTensorPin projectionBiasPin =
2613 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2614 17,
2615 model,
2616 data,
2617 g_DontPermute,
2618 nullptr,
2619 true);
2620
2621 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2622 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2623 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2624 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2625 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2626 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2627 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2628 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2629 {
2630 return Fail("%s: Operation has invalid tensor inputs", __func__);
2631 }
2632
2633 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2634 // 20: The activation function: A value indicating the activation function:
2635 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2636 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2637 // If set to 0.0 then clipping is disabled.
2638 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2639 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2640 ActivationFn activation;
2641 float cellClip;
2642 float projClip;
2643 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2644 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2645 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2646 {
2647 return Fail("%s: Operation has invalid scalar inputs", __func__);
2648 }
2649
2650 // Get the normalization tensors
2651 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2652 // Used to rescale normalized inputs to activation at input gate.
2653 const ConstTensorPin inputLayerNormWeightsPin
2654 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2655
2656 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2657 // Used to rescale normalized inputs to activation at forget gate.
2658 const ConstTensorPin forgetLayerNormWeightsPin =
2659 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2660 24,
2661 model,
2662 data,
2663 g_DontPermute,
2664 nullptr,
2665 true);
2666
2667 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2668 // Used to rescale normalized inputs to activation at cell gate.
2669 const ConstTensorPin cellLayerNormWeightsPin =
2670 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2671 25,
2672 model,
2673 data,
2674 g_DontPermute,
2675 nullptr,
2676 true);
2677
2678 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2679 // Used to rescale normalized inputs to activation at output gate.
2680 const ConstTensorPin outputLayerNormWeightsPin =
2681 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2682 26,
2683 model,
2684 data,
2685 g_DontPermute,
2686 nullptr,
2687 true);
2688
2689 // Outputs:
2690 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2691 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2692 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2693 if (!scratchBuffer)
2694 {
2695 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2696 }
2697 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2698 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2699 if (!outputStateOut)
2700 {
2701 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2702 }
2703 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2704 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2705 if (!cellStateOut)
2706 {
2707 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2708 }
2709 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2710 // effectively the same as the current “output state (out)” value.
2711 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2712 if (!output)
2713 {
2714 return Fail("%s: Could not read output 3: output", __func__);
2715 }
2716
2717 // set the params structure for the AddLstmLayer call
2718 LstmInputParams params;
2719 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2720 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2721 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2722 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2723 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2724 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2725 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2726 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2727 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2728 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2729 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2730 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2731 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2732 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2733 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2734 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2735 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2736 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2737 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2738 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2739 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2740
2741 // set the layer descriptor
2742 LstmDescriptor desc;
2743 desc.m_ActivationFunc = activation;
2744 desc.m_ClippingThresCell = cellClip;
2745 desc.m_ClippingThresProj = projClip;
2746 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2747 params.m_RecurrentToInputWeights == nullptr ||
2748 params.m_InputGateBias == nullptr);
2749 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2750 params.m_CellToOutputWeights != nullptr);
2751 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2752 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2753 params.m_ForgetLayerNormWeights != nullptr ||
2754 params.m_CellLayerNormWeights != nullptr ||
2755 params.m_OutputLayerNormWeights != nullptr);
2756
2757 // validate the optional input groups
2758 if (desc.m_CifgEnabled &&
2759 (params.m_InputToInputWeights != nullptr ||
2760 params.m_RecurrentToInputWeights != nullptr ||
2761 params.m_InputGateBias != nullptr))
2762 {
2763 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2764 " and input gate bias must be provided", __func__);
2765 }
2766
2767 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2768 {
2769 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2770 }
2771
2772 if (desc.m_PeepholeEnabled &&
2773 (params.m_CellToForgetWeights == nullptr ||
2774 params.m_CellToOutputWeights == nullptr ||
2775 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2776 {
2777 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2778 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2779 }
2780
2781 if (desc.m_LayerNormEnabled &&
2782 (params.m_ForgetLayerNormWeights == nullptr ||
2783 params.m_CellLayerNormWeights == nullptr ||
2784 params.m_OutputLayerNormWeights == nullptr ||
2785 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2786 {
2787 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2788 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2789 }
2790
2791 // Check if the layer is supported
2792 // Inputs
2793 const TensorInfo& inputInfo = input.GetTensorInfo();
2794 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2795 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2796
2797 // Outputs
2798 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2799 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2800 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2801 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2802
Kevin May42477c12020-03-26 13:34:14 +00002803 // Basic parameters
2804 LstmInputParamsInfo paramsInfo;
2805 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2806 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2807 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2808 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2809 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2810 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2811 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2812 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2813 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2814
2815 // Optional parameters
2816 if (!desc.m_CifgEnabled)
2817 {
2818 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2819 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2820 if (params.m_CellToInputWeights != nullptr)
2821 {
2822 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2823 }
2824 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2825 }
2826
2827 if (desc.m_ProjectionEnabled)
2828 {
2829 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2830 if (params.m_ProjectionBias != nullptr)
2831 {
2832 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2833 }
2834 }
2835
2836 if (desc.m_PeepholeEnabled)
2837 {
2838 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2839 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2840 }
2841
2842 if (desc.m_LayerNormEnabled)
2843 {
2844 if(!desc.m_CifgEnabled)
2845 {
2846 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2847 }
2848 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2849 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2850 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2851 }
2852
2853 bool isSupported = false;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002854 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2855 {
2856 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2857 IsLstmSupported,
2858 data.m_Backends,
2859 isSupported,
2860 inputInfo,
2861 outputStateInInfo,
2862 cellStateInInfo,
2863 scratchBufferInfo,
2864 outputStateOutInfo,
2865 cellStateOutInfo,
2866 outputInfo,
2867 desc,
2868 paramsInfo);
2869 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002870
Sadik Armagandbda4b72020-09-03 11:33:07 +01002871 bool isDynamic = false;
2872 if (!IsDynamicTensor(outputStateOutInfo) &&
2873 !IsDynamicTensor(scratchBufferInfo) &&
2874 !IsDynamicTensor(cellStateOutInfo) &&
2875 !IsDynamicTensor(outputInfo))
2876 {
2877 validateFunc(outputInfo, isSupported);
2878 }
2879 else
2880 {
2881 isDynamic = true;
2882 isSupported = AreDynamicTensorsSupported();
2883 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002884
Kevin May42477c12020-03-26 13:34:14 +00002885 if (!isSupported)
2886 {
2887 return false;
2888 }
2889
2890 // Add the layer
2891 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2892
2893 input.Connect(layer->GetInputSlot(0));
2894 outputStateIn.Connect(layer->GetInputSlot(1));
2895 cellStateIn.Connect(layer->GetInputSlot(2));
2896
Sadik Armagandbda4b72020-09-03 11:33:07 +01002897 if (!isDynamic)
2898 {
2899 return (
2900 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2901 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2902 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2903 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2904 }
2905 else
2906 {
2907 return (
2908 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2909 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2910 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2911 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002912 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002913 }
2914
Kevin May42477c12020-03-26 13:34:14 +00002915}
2916
2917template<typename HalPolicy,
2918 typename HalOperation = typename HalPolicy::Operation,
2919 typename HalModel = typename HalPolicy::Model>
2920bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2921{
2922 using HalOperand = typename HalPolicy::Operand;
2923 using HalOperandType = typename HalPolicy::OperandType;
2924
2925 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2926
2927 if (!input.IsValid())
2928 {
2929 return Fail("%s: Operation has invalid inputs", __func__);
2930 }
2931
2932 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2933
2934 if (!output)
2935 {
2936 return Fail("%s: Could not read output 0", __func__);
2937 }
2938
2939 const TensorInfo& inputInfo = input.GetTensorInfo();
2940 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002941
2942 // ArmNN does not currently support non-fixed weights or bias
2943 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2944 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2945
2946 if (weightsOperand == nullptr)
2947 {
2948 return Fail("%s: Operand is invalid", __func__);
2949 }
2950 TransposeConvolution2dDescriptor desc;
2951 desc.m_DataLayout = DataLayout::NHWC;
2952
2953 // Determine whether padding is implicit or explicit
2954 bool implicitPadding = operation.inputs.size() == 9;
2955
2956 if (implicitPadding )
2957 {
2958 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2959 }
2960 else
2961 {
2962 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2963 }
2964
2965 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2966 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2967 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2968
2969 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2970
2971 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2972 // We have to permute it to OIHW if the data layout is NCHW.
2973 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2974 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2975 model, data, OHWIToOIHW) :
2976 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2977
2978 // Bias is a 1D tensor
2979 const ConstTensorPin biasPin =
2980 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2981
2982 if (!weightsPin.IsValid())
2983 {
2984 return Fail("%s: Operation has invalid weights", __func__);
2985 }
2986
2987 if (!biasPin.IsValid())
2988 {
2989 return Fail("%s: Operation has invalid biases", __func__);
2990 }
2991
2992 ConstTensor weights = weightsPin.GetConstTensor();
2993 ConstTensor bias = biasPin.GetConstTensor();
2994 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2995
2996 ActivationFn activation;
2997
2998 if (implicitPadding)
2999 {
3000 int32_t strideX{0};
3001 int32_t strideY{0};
3002 int32_t padLeft{0};
3003 int32_t padRight{0};
3004 int32_t padTop{0};
3005 int32_t padBottom{0};
3006
3007 android::nn::PaddingScheme paddingScheme;
3008 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
3009 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
3010 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
3011 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
3012 {
3013 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
3014 }
3015
3016 const uint32_t kernelX = weights.GetShape()[widthIndex];
3017 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00003018
Colm Donelan8f3d33e2020-07-06 15:41:43 +01003019 // If output shape has been specified as a parameter then extract it and make it available.
3020 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
3021 std::vector<int32_t> outputShape;
3022 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
3023 {
3024 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
3025 for (int dimension : outputShape)
3026 {
3027 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
3028 }
3029 desc.m_OutputShapeEnabled = true;
3030 }
3031
Finn Williams8fe50c62020-10-09 15:52:57 +01003032 uint32_t outputX;
3033 uint32_t outputY;
3034
3035 if (IsDynamicTensor(outputInfo))
3036 {
3037 if (outputShape.size() == 0)
3038 {
3039 return Fail("%s: Padding sizes cannot be inferred", __func__);
3040 }
3041
3042 outputX = outputShape[widthIndex];
3043 outputY = outputShape[heightIndex];
3044 }
3045 else
3046 {
3047 outputX = outputInfo.GetShape()[widthIndex];
3048 outputY = outputInfo.GetShape()[heightIndex];
3049 }
3050
3051 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
3052 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
3053
3054 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
3055 // but Arm NN only supports values >= 0
3056 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
3057 {
3058 return Fail("%s: Negative padding values are not supported", __func__);
3059 }
3060
Matthew Sloyan9b088d92020-09-14 15:12:55 +01003061 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
3062 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
3063 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
3064 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
3065 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
3066 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00003067 }
3068 else if (operation.inputs.size() == 11)
3069 {
3070 // explicit padding
3071 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
3072 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
3073 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
3074 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
3075 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
3076 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
3077 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
3078 {
3079 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
3080 }
3081 }
3082 else
3083 {
3084 return Fail("%s: Unsupported number of operation inputs", __func__);
3085 }
3086
3087 desc.m_BiasEnabled = true;
3088 Optional<TensorInfo> biases(bias.GetInfo());
3089
3090 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003091 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3092 {
3093 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3094 IsTransposeConvolution2dSupported,
3095 data.m_Backends,
3096 isSupported,
3097 inputInfo,
3098 outputInfo,
3099 desc,
3100 weights.GetInfo(),
3101 biases);
3102 };
3103
3104 if(IsDynamicTensor(outputInfo))
3105 {
3106 isSupported = AreDynamicTensorsSupported();
3107 }
3108 else
3109 {
3110 validateFunc(outputInfo, isSupported);
3111 }
Kevin May42477c12020-03-26 13:34:14 +00003112 if (!isSupported)
3113 {
3114 return false;
3115 }
3116
3117 IConnectableLayer* startLayer =
3118 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
3119 if (!startLayer)
3120 {
3121 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
3122 }
3123
Kevin May42477c12020-03-26 13:34:14 +00003124 input.Connect(startLayer->GetInputSlot(0));
3125
Kevin Mayfcf2a152020-09-08 16:06:32 +01003126 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
3127 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00003128}
3129
3130} // armnn_driver namespace