blob: 404ff32d0396866943e052424bc5be40d63164d8 [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();
166 assert(layer != nullptr);
167 input.Connect(layer->GetInputSlot(0));
168
169 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
170}
171
172template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000173 typename HalOperation = typename HalPolicy::Operation,
174 typename HalModel = typename HalPolicy::Model>
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100175bool ConvertChannelShuffle(const HalOperation& operation,
176 const HalModel& model,
177 ConversionData& data)
178{
179 using HalOperand = typename HalPolicy::Operand;
180 using HalOperandType = typename HalPolicy::OperandType;
181
182 ALOGV("HalPolicy::ConvertChannelShuffle()");
183
184 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
185 if (!input.IsValid())
186 {
187 return Fail("%s: Operation has invalid inputs", __func__);
188 }
189 auto inputDimensions = static_cast<int32_t>(input.GetTensorInfo().GetNumDimensions());
190
191 ChannelShuffleDescriptor descriptor;
192
193 int32_t groups;
194 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, groups, model, data))
195 {
196 return Fail("%s: Operation has invalid or unsupported number of groups operand", __func__);
197 }
198 descriptor.m_NumGroups = static_cast<uint32_t>(groups);
199
200 int32_t axis;
201 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, axis, model, data))
202 {
203 return Fail("%s: Operation has invalid or unsupported dimension channel shuffle operand", __func__);
204 }
205 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
206 {
207 return Fail("%s: Operation has invalid dimension: %d. It is out of bounds [-%d, %d))", __func__, axis,
208 inputDimensions, inputDimensions);
209 }
210 int positiveAxis = (axis < 0) ? inputDimensions + axis : axis;
211 descriptor.m_Axis = static_cast<uint32_t>(positiveAxis);
212
213 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
214 if (!output)
215 {
216 return Fail("%s: Could not read output 0", __func__);
217 }
218
219 const TensorInfo& inputInfo = input.GetTensorInfo();
220 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
221
222 bool isSupported = false;
223
224 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
225 {
226 FORWARD_LAYER_SUPPORT_FUNC(__func__,
227 IsChannelShuffleSupported,
228 data.m_Backends,
229 isSupported,
230 inputInfo,
231 outputInfo,
232 descriptor);
233 };
234
235 if(!IsDynamicTensor(outputInfo))
236 {
237 validateFunc(outputInfo, isSupported);
238 }
239 else
240 {
241 isSupported = AreDynamicTensorsSupported();
242 }
243
244 if (!isSupported)
245 {
246 return false;
247 }
248
249 IConnectableLayer* layer = data.m_Network->AddChannelShuffleLayer(descriptor);
250 assert(layer != nullptr);
251 input.Connect(layer->GetInputSlot(0));
252
253 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
254}
255
256template<typename HalPolicy,
257 typename HalOperation = typename HalPolicy::Operation,
258 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +0000259bool ConvertComparison_1_2(const HalOperation& operation,
260 const HalModel& model,
261 ConversionData& data,
262 ComparisonOperation comparisonOperation)
263{
264 using HalOperand = typename HalPolicy::Operand;
265
266 ALOGV("HalPolicy::ConvertComparison()");
267 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
268
269 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
270 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
271
272 if (!(input0.IsValid() && input1.IsValid()))
273 {
274 return Fail("%s: Operation has invalid inputs", __func__);
275 }
276
277 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
278 if (!output)
279 {
280 return Fail("%s: Could not read output 0", __func__);
281 }
282
283 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
284 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
285 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
286
Kevin May42477c12020-03-26 13:34:14 +0000287 ComparisonDescriptor descriptor(comparisonOperation);
288
289 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100290 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
291 {
292 FORWARD_LAYER_SUPPORT_FUNC(__func__,
293 IsComparisonSupported,
294 data.m_Backends,
295 isSupported,
296 inputInfo0,
297 inputInfo1,
298 outputInfo,
299 descriptor);
300
301 };
302
303 if(!IsDynamicTensor(outputInfo))
304 {
305 validateFunc(outputInfo, isSupported);
306 }
307 else
308 {
309 isSupported = AreDynamicTensorsSupported();
310 }
Kevin May42477c12020-03-26 13:34:14 +0000311
312 if (!isSupported)
313 {
314 return false;
315 }
316
317 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
318 assert(layer != nullptr);
319
320 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
321 if (!isReshapeSupported)
322 {
323 return false;
324 }
325
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100326 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000327}
328
329template<typename HalPolicy,
330 typename HalOperation = typename HalPolicy::Operation,
331 typename HalModel = typename HalPolicy::Model>
332bool ConvertConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
333{
334
335 using HalOperand = typename HalPolicy::Operand;
336 using HalOperandType = typename HalPolicy::OperandType;
337
338 ALOGV("HalPolicy::ConvertConv2d_1_2()");
339
340 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
341 if (!input.IsValid())
342 {
343 return Fail("%s: Operation has invalid inputs", __func__);
344 }
345
346 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
347 if (!output)
348 {
349 return Fail("%s: Could not read output 0", __func__);
350 }
351
352 const TensorInfo& inputInfo = input.GetTensorInfo();
353 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
354
Kevin May42477c12020-03-26 13:34:14 +0000355 Convolution2dDescriptor desc;
356 desc.m_DataLayout = DataLayout::NHWC;
357
358 // Determine whether padding is implicit or explicit
359 bool implicitPadding = operation.inputs.size() == 7 ||
360 (operation.inputs.size() >= 8 &&
361 GetInputOperand<HalPolicy>(operation, 7, model)->type == HalOperandType::BOOL);
362
363 if (implicitPadding)
364 {
365 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
366 }
367 else if (operation.inputs.size() >= 10)
368 {
369 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
370 }
371
372 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
373
374 // ArmNN does not currently support non-fixed weights or bias
375 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
376 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
377 // the DataLayout is NCHW
378 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
379 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
380 model, data, OHWIToOIHW) :
381 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
382 const ConstTensorPin biasPin =
383 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
384
385 if (!weightsPin.IsValid())
386 {
387 return Fail("%s: Operation has invalid weights", __func__);
388 }
389
390 if (!biasPin.IsValid())
391 {
392 return Fail("%s: Operation has invalid biases", __func__);
393 }
394
395 ConstTensor weights = weightsPin.GetConstTensor();
396 ConstTensor bias = biasPin.GetConstTensor();
397 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
398
399 ActivationFn activation;
400
401 if (implicitPadding)
402 {
403 android::nn::PaddingScheme paddingScheme;
404 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
405 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
406 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
407 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data) ||
408 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 8, desc, model, data))
409 {
410 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
411 }
412
413 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
414 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
415 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
416 const uint32_t kernelX = weights.GetShape()[widthIndex];
417 const uint32_t kernelY = weights.GetShape()[heightIndex];
418 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
419 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
420
421 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
422 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
423
424 }
425 else if (operation.inputs.size() >= 10)
426 {
427 // explicit padding
428 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
429 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
430 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
431 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
432 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
433 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
434 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data) ||
435 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 11, desc, model, data))
436 {
437 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
438 }
439 }
440 else
441 {
442 return Fail("%s: Unsupported number of operation inputs", __func__);
443 }
444
445 desc.m_BiasEnabled = true;
446 Optional<TensorInfo> biases(bias.GetInfo());
447
448 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100449 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
450 {
451 FORWARD_LAYER_SUPPORT_FUNC(__func__,
452 IsConvolution2dSupported,
453 data.m_Backends,
454 isSupported,
455 inputInfo,
456 outputInfo,
457 desc,
458 weights.GetInfo(),
459 biases);
460 };
461
462 if(!IsDynamicTensor(outputInfo))
463 {
464 validateFunc(outputInfo, isSupported);
465 }
466 else
467 {
468 isSupported = AreDynamicTensorsSupported();
469 }
Kevin May42477c12020-03-26 13:34:14 +0000470
471 if (!isSupported)
472 {
473 return false;
474 }
475
476 IConnectableLayer* startLayer =
477 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
478
479 if (!startLayer)
480 {
481 return Fail("%s: AddConvolution2dLayer failed", __func__);
482 }
483
Kevin May42477c12020-03-26 13:34:14 +0000484 input.Connect(startLayer->GetInputSlot(0));
485
Kevin Mayfcf2a152020-09-08 16:06:32 +0100486 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
487 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000488}
489
490template<typename HalPolicy,
491 typename HalOperation = typename HalPolicy::Operation,
492 typename HalModel = typename HalPolicy::Model>
493bool ConvertDepthwiseConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
494{
495 using HalOperand = typename HalPolicy::Operand;
496 using HalOperandType = typename HalPolicy::OperandType;
497
498 ALOGV("HalPolicy::ConvertDepthwiseConv2d_1_2()");
499
500 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
501
502 if (!input.IsValid())
503 {
504 return Fail("%s: Operation has invalid inputs", __func__);
505 }
506
507 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
508
509 if (!output)
510 {
511 return Fail("%s: Could not read output 0", __func__);
512 }
513
514 const TensorInfo& inputInfo = input.GetTensorInfo();
515 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
516
Kevin May42477c12020-03-26 13:34:14 +0000517 // ArmNN does not currently support non-fixed weights or bias
518 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
519 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
520
521 if (weightsOperand == nullptr)
522 {
523 return Fail("%s: Operand is invalid", __func__);
524 }
525 if ( weightsOperand->dimensions[0] != 1)
526 {
527 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
528 __func__, weightsOperand->dimensions[0] );
529 }
530
531 DepthwiseConvolution2dDescriptor desc;
532 desc.m_DataLayout = DataLayout::NHWC;
533
534 // Determine whether padding is implicit or explicit
535 bool implicitPadding = operation.inputs.size() == 8 ||
536 (operation.inputs.size() >= 9 &&
537 GetInputOperand<HalPolicy>(operation, 8, model)->type == HalOperandType::BOOL);
538
539 // Look ahead to find the optional DataLayout, if present
540 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
541 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, dataLayoutFlagIndex, model, data);
542
543 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
Kevin May42477c12020-03-26 13:34:14 +0000544 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
545 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
546
Jan Eilersa20d2b82021-04-27 09:21:08 +0100547 // 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 +0000548 const ConstTensorPin weightsPin =
549 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
550 1,
551 model,
Jan Eilersa20d2b82021-04-27 09:21:08 +0100552 data);
Kevin May42477c12020-03-26 13:34:14 +0000553
554 // Bias is a 1D tensor
555 const ConstTensorPin biasPin =
556 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
557
558 if (!weightsPin.IsValid())
559 {
560 return Fail("%s: Operation has invalid weights", __func__);
561 }
562
563 if (!biasPin.IsValid())
564 {
565 return Fail("%s: Operation has invalid biases", __func__);
566 }
567
568 ConstTensor weights = weightsPin.GetConstTensor();
569 ConstTensor bias = biasPin.GetConstTensor();
570 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
571
572 ActivationFn activation;
573
574 if (implicitPadding)
575 {
576 android::nn::PaddingScheme paddingScheme;
577 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
578 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
579 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
580 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data) ||
581 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 9, desc, model, data))
582 {
583 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
584 }
585
Jan Eilersa20d2b82021-04-27 09:21:08 +0100586 const uint32_t kernelX = weights.GetShape()[2];
587 const uint32_t kernelY = weights.GetShape()[1];
Kevin May42477c12020-03-26 13:34:14 +0000588 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
589 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
590
591 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
592 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
593 }
594 else if (operation.inputs.size() >= 11)
595 {
596 // explicit padding
597 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
598 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
599 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
600 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
601 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
602 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
603 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data) ||
604 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 12, desc, model, data))
605 {
606 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
607 }
608 }
609 else
610 {
611 return Fail("%s: Unsupported number of operation inputs", __func__);
612 }
613
614 desc.m_BiasEnabled = true;
615 Optional<TensorInfo> biases(bias.GetInfo());
616
617 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100618 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
619 {
620 FORWARD_LAYER_SUPPORT_FUNC(__func__,
621 IsDepthwiseConvolutionSupported,
622 data.m_Backends,
623 isSupported,
624 inputInfo,
625 outputInfo,
626 desc,
627 weights.GetInfo(),
628 biases);
629 };
630
631 if(!IsDynamicTensor(outputInfo))
632 {
633 validateFunc(outputInfo, isSupported);
634 }
635 else
636 {
637 isSupported = AreDynamicTensorsSupported();
638 }
Kevin May42477c12020-03-26 13:34:14 +0000639
640 if (!isSupported)
641 {
642 return false;
643 }
644
645 IConnectableLayer* startLayer =
646 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
647
648 if (!startLayer)
649 {
650 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
651 }
652
Kevin May42477c12020-03-26 13:34:14 +0000653 input.Connect(startLayer->GetInputSlot(0));
654
Kevin Mayfcf2a152020-09-08 16:06:32 +0100655 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
656 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000657}
658
659template<typename HalPolicy,
660 typename HalOperation = typename HalPolicy::Operation,
661 typename HalModel = typename HalPolicy::Model>
662bool ConvertDequantize_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
663{
664 ALOGV("HalPolicy::ConvertDequantize()");
665
666 if (IsQSymmDequantizeForWeights<HalPolicy>(operation, model))
667 {
668 // NOTE: QSymm8 weights are dequantized internally by the driver,
669 // therefore this type of Dequantize is implicitly supported
670 return true;
671 }
672
673 return ::ConvertDequantize<HalPolicy>(operation, model, data);
674}
675
676template<typename HalPolicy,
677 typename HalOperation = typename HalPolicy::Operation,
678 typename HalModel = typename HalPolicy::Model>
679bool ConvertElementwiseUnary(const HalOperation& operation,
680 const HalModel& model,
681 ConversionData& data,
682 UnaryOperation unaryOperation)
683{
684 using HalOperand = typename HalPolicy::Operand;
685
686 ALOGV("HalPolicy::ConvertElementwiseUnary()");
687 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
688
689 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
690
691 if (!input.IsValid())
692 {
693 return Fail("%s: Operation has invalid input", __func__);
694 }
695
696 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
697 if (!output)
698 {
699 return Fail("%s: Could not read output 0", __func__);
700 }
701
702 const TensorInfo& inputInfo = input.GetTensorInfo();
703 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
704
Kevin May42477c12020-03-26 13:34:14 +0000705 ElementwiseUnaryDescriptor descriptor(unaryOperation);
706
707 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100708
709 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
710 {
711 FORWARD_LAYER_SUPPORT_FUNC(__func__,
712 IsElementwiseUnarySupported,
713 data.m_Backends,
714 isSupported,
715 inputInfo,
716 outputInfo,
717 descriptor);
718 };
719
720 if(!IsDynamicTensor(outputInfo))
721 {
722 validateFunc(outputInfo, isSupported);
723 }
724 else
725 {
726 isSupported = AreDynamicTensorsSupported();
727 }
Kevin May42477c12020-03-26 13:34:14 +0000728
729 if (!isSupported)
730 {
731 return false;
732 }
733
734 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
735 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +0000736 input.Connect(layer->GetInputSlot(0));
737
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100738 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000739}
740
741template<typename HalPolicy,
742 typename HalOperation = typename HalPolicy::Operation,
743 typename HalModel = typename HalPolicy::Model>
744bool ConvertExpandDims(const HalOperation& operation, const HalModel& model, ConversionData& data)
745{
746 using HalOperand = typename HalPolicy::Operand;
747 using HalOperandType = typename HalPolicy::OperandType;
748
749 ALOGV("HalPolicy::ConvertExpandDims()");
750
751 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
752
753 if (!input.IsValid())
754 {
755 return Fail("%s: Operation has invalid input", __func__);
756 }
757
758 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
759 if (!output)
760 {
761 return Fail("%s: Operation has invalid output", __func__);
762 }
763
764 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000765
766 int32_t axis;
767 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
768 {
769 return Fail("%s: failed to get axis input value", __func__);
770 }
771
772 TensorShape targetShape;
773
774 try
775 {
776 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
777 }
778 catch (const std::exception& e)
779 {
780 return Fail("%s: %s", __func__, e.what());
781 }
782
Kevin May42477c12020-03-26 13:34:14 +0000783 ReshapeDescriptor reshapeDescriptor;
784 reshapeDescriptor.m_TargetShape = targetShape;
785
786 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100787 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
788 {
789 FORWARD_LAYER_SUPPORT_FUNC(__func__,
790 IsReshapeSupported,
791 data.m_Backends,
792 isSupported,
793 input.GetTensorInfo(),
794 outputInfo,
795 reshapeDescriptor);
796 };
797
798 if(!IsDynamicTensor(outputInfo))
799 {
Nikhil Rajc5e0bb02021-04-02 10:02:16 +0100800 if (targetShape != outputInfo.GetShape())
801 {
802 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
803 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100804 validateFunc(outputInfo, isSupported);
805 }
806 else
807 {
808 isSupported = AreDynamicTensorsSupported();
809 }
Kevin May42477c12020-03-26 13:34:14 +0000810
811 if (!isSupported)
812 {
813 return false;
814 }
815
816 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
817 assert(layer != nullptr);
818 input.Connect(layer->GetInputSlot(0));
819
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100820 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000821}
822
823template<typename HalPolicy,
Teresa Charlinf931af92020-04-10 16:46:53 +0100824 typename HalOperation = typename HalPolicy::Operation,
825 typename HalModel = typename HalPolicy::Model>
826bool ConvertGather(const HalOperation& operation, const HalModel& model, ConversionData& data)
827{
828 using HalOperand = typename HalPolicy::Operand;
829 using HalOperandType = typename HalPolicy::OperandType;
830
831 ALOGV("HalPolicy::ConvertGather()");
832
833 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
834 if (!input.IsValid())
835 {
836 return Fail("%s: Operation has invalid input", __func__);
837 }
838 auto inputDimensions = input.GetTensorInfo().GetNumDimensions();
839
840 LayerInputHandle indices = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data);
841 if (!indices.IsValid())
842 {
843 return Fail("%s: Operation has invalid indices", __func__);
844 }
845 auto indicesDimensions = indices.GetTensorInfo().GetNumDimensions();
846
847 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
848 if (!output)
849 {
850 return Fail("%s: Operation has invalid output", __func__);
851 }
852 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
853 auto outputDimensions = outputInfo.GetNumDimensions();
Teresa Charlinf931af92020-04-10 16:46:53 +0100854 if (outputDimensions != inputDimensions + indicesDimensions - 1)
855 {
856 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 +0100857 __func__, outputDimensions, inputDimensions, indicesDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100858 }
859
Finn Williamsf769f292021-06-25 12:53:09 +0100860 uint32_t axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100861 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
862 {
863 return Fail("%s: Operation has invalid or unsupported axis operand", __func__);
864 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100865 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
Teresa Charlinf931af92020-04-10 16:46:53 +0100866 {
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100867 return Fail("%s: Operation has invalid axis: %d. It is out of bounds [-%d, %d))", __func__, axis,
868 inputDimensions, inputDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100869 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100870
871 GatherDescriptor desc;
872 desc.m_Axis = axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100873
874 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100875 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
876 {
877 FORWARD_LAYER_SUPPORT_FUNC(__func__,
878 IsGatherSupported,
879 data.m_Backends,
880 isSupported,
881 input.GetTensorInfo(),
882 indices.GetTensorInfo(),
883 outputInfo,
884 desc);
885 };
886
887 if(!IsDynamicTensor(outputInfo))
888 {
889 validateFunc(outputInfo, isSupported);
890 }
891 else
892 {
893 isSupported = AreDynamicTensorsSupported();
894 }
895
Teresa Charlinf931af92020-04-10 16:46:53 +0100896 if (!isSupported)
897 {
898 return false;
899 }
900
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100901 IConnectableLayer* layer = data.m_Network->AddGatherLayer(desc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100902 assert(layer != nullptr);
903 input.Connect(layer->GetInputSlot(0));
904 indices.Connect(layer->GetInputSlot(1));
905
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100906 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100907}
908
909template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000910 typename HalOperation = typename HalPolicy::Operation,
911 typename HalModel = typename HalPolicy::Model>
912bool ConvertGroupedConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
913{
914 using HalOperand = typename HalPolicy::Operand;
915 using HalOperandType = typename HalPolicy::OperandType;
916
917 ALOGV("HalPolicy::ConvertGroupedConv2d()");
918
919 //
920 // Parse data
921 //
922 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
923 if (!input.IsValid())
924 {
925 return Fail("%s: Operation has invalid inputs", __func__);
926 }
927 const TensorInfo& inputInfo = input.GetTensorInfo();
928
929 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
930 if (!output)
931 {
932 return Fail("%s: Could not read output 0", __func__);
933 }
Finn Williamsb0331172020-10-08 14:33:13 +0100934 TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000935
936 // Look ahead to determine data layout
937 DataLayout dataLayout = DataLayout::NHWC;
938 if (operation.inputs.size() == 12)
939 {
940 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
941 }
942 else
943 {
944 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
945 }
946
947 // NOTE:
948 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
949 // but Arm NN expects the filter's height and width indices to match the input's height and
950 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
951 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
952 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
953 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
954 model, data, ohwiToOihw) :
955 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
956 const ConstTensorPin biasesPin =
957 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
958 if (!weightsPin.IsValid() || !biasesPin.IsValid())
959 {
960 return Fail("%s: Operation has invalid inputs", __func__);
961 }
962
963 ConstTensor weights = weightsPin.GetConstTensor();
964 ConstTensor biases = biasesPin.GetConstTensor();
965 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
966
967 const TensorShape& inputShape = inputInfo.GetShape();
968 const TensorShape& outputShape = outputInfo.GetShape();
969 const TensorShape& weightsShape = weights.GetShape();
Kevin May42477c12020-03-26 13:34:14 +0000970
971 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
972 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
973 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
974 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
975
976 Convolution2dDescriptor desc;
977 desc.m_DataLayout = dataLayout;
978 desc.m_BiasEnabled = true;
979
Finn Williamsf769f292021-06-25 12:53:09 +0100980 unsigned int numGroups;
Kevin May42477c12020-03-26 13:34:14 +0000981 ActivationFn activation;
982
983 if (operation.inputs.size() == 12)
984 {
985 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
986 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
987 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
988 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
989 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
990 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
991 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
992 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
993 {
994 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
995 }
996
997 }
998 else if (operation.inputs.size() == 9)
999 {
1000 android::nn::PaddingScheme paddingScheme;
1001 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1002 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1003 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1004 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
1005 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
1006 {
1007 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1008 }
1009
1010 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
1011 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
1012
1013 const uint32_t kernelX = weightsShape[widthIndex];
1014 const uint32_t kernelY = weightsShape[heightIndex];
1015
1016 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1017 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1018 }
1019 else
1020 {
1021 return Fail("%s: Unsupported number of operation inputs", __func__);
1022 }
1023
Finn Williamsb0331172020-10-08 14:33:13 +01001024 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1025 const unsigned int outputChannels = weightsShape[0];
Kevin May42477c12020-03-26 13:34:14 +00001026
1027 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
1028 const unsigned int channelMultiplier = outputChannels / numGroups;
1029
1030 //
1031 // Validate all relevant inputs
1032 //
1033 if (numGroups <= 0)
1034 {
1035 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
1036 }
1037
1038 if (outputChannels % numGroups != 0u)
1039 {
1040 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
1041 }
1042
1043 //
1044 // Set up Splitter layer
1045 //
1046 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
1047 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
1048
1049 TensorInfo splitterOutputInfo(4,
1050 splitterDimSizes,
1051 inputInfo.GetDataType(),
1052 inputInfo.GetQuantizationScale(),
1053 inputInfo.GetQuantizationOffset());
1054
1055 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
1056
1057 ViewsDescriptor splitterDesc(numGroups);
1058 for (unsigned int group = 0u; group < numGroups; ++group)
1059 {
1060 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
1061 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
1062 {
1063 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
1064 }
1065 }
1066
1067 bool isSupported = false;
1068 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1069 IsSplitterSupported,
1070 data.m_Backends,
1071 isSupported,
1072 inputInfo,
1073 splitterOutputInfos,
1074 splitterDesc);
1075 if (!isSupported)
1076 {
1077 return false;
1078 }
1079
1080 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
1081 if (!splitterLayer)
1082 {
1083 return Fail("%s: Failed to add SplitterLayer", __func__);
1084 }
1085
1086 input.Connect(splitterLayer->GetInputSlot(0));
1087 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
1088 {
1089 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
1090 }
1091
1092 //
1093 // Set up Convolution2d layers for each group
1094 //
1095
1096 // Set up group tensor shapes
1097 TensorShape groupInputShape(inputShape);
1098 groupInputShape[channelsIndex] = channelsPerGroup;
1099
Kevin May42477c12020-03-26 13:34:14 +00001100 TensorShape groupWeightsShape(weightsShape);
1101 groupWeightsShape[0] /= channelMultiplier * numGroups;
1102
1103 TensorShape groupBiasesShape({ 1 });
1104
1105 // Set up group tensor infos
1106 TensorInfo groupInputInfo(inputInfo);
1107 groupInputInfo.SetShape(groupInputShape);
1108
1109 const TensorInfo& weightsInfo = weights.GetInfo();
1110 TensorInfo groupWeightsInfo(weightsInfo);
1111 groupWeightsInfo.SetShape(groupWeightsShape);
1112
1113 const TensorInfo& biasesInfo = biases.GetInfo();
1114 TensorInfo groupBiasesInfo(biasesInfo);
1115 groupBiasesInfo.SetShape(groupBiasesShape);
1116
1117 TensorInfo groupOutputInfo(outputInfo);
Finn Williamsb0331172020-10-08 14:33:13 +01001118
1119 TensorShape groupOutputShape(outputShape);
1120 const bool isDynamic = IsDynamicTensor(outputInfo);
1121 if (!isDynamic)
1122 {
1123 groupOutputShape[channelsIndex] = 1;
1124 }
Kevin May42477c12020-03-26 13:34:14 +00001125 groupOutputInfo.SetShape(groupOutputShape);
1126
1127 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1128 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1129
1130 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1131 for (unsigned int group = 0u; group < numGroups; ++group)
1132 {
1133 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1134 {
1135 auto index = group * channelMultiplier + m;
1136
1137 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1138 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1139
1140 if (weightsInfo.HasPerAxisQuantization())
1141 {
1142 // Extract per-axis quantization scales for group weights
1143 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1144 groupWeightsInfo.SetQuantizationScales(
1145 std::vector<float>(weightsQuantScales.begin() + index,
1146 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1147
1148 // Extract per-axis quantization scales for group biases
1149 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1150 groupBiasesInfo.SetQuantizationScales(
1151 std::vector<float>(biasesQuantScales.begin() + index,
1152 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1153 }
1154
1155 // Extract weights and biases data for current group convolution
1156 ConstTensor groupWeights(groupWeightsInfo,
1157 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1158 weightsDataOffset));
1159 ConstTensor groupBiases(groupBiasesInfo,
1160 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1161 biasesDataOffset));
1162
1163 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001164 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1165 {
1166 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1167 IsConvolution2dSupported,
1168 data.m_Backends,
1169 isSupported,
1170 groupInputInfo,
1171 outputInfo,
1172 desc,
1173 groupWeightsInfo,
1174 Optional<TensorInfo>(groupBiasesInfo));
1175 };
1176
1177 if(!isDynamic)
1178 {
1179 validateFunc(groupOutputInfo, isSupported);
1180 }
1181 else
1182 {
1183 isSupported = AreDynamicTensorsSupported();
1184 }
1185
Kevin May42477c12020-03-26 13:34:14 +00001186 if (!isSupported)
1187 {
1188 return false;
1189 }
1190
1191 IConnectableLayer* convLayer =
1192 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1193 if (!convLayer)
1194 {
1195 return Fail("%s: AddConvolution2dLayer failed", __func__);
1196 }
1197
1198 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1199 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1200
Finn Williamsb0331172020-10-08 14:33:13 +01001201 if(isDynamic)
1202 {
1203 convLayer->GetOutputSlot(0).IsTensorInfoSet();
1204
1205 validateFunc(convLayer->GetOutputSlot(0).GetTensorInfo(), isSupported);
1206
1207 outputInfo = convLayer->GetOutputSlot(0).GetTensorInfo();
1208
1209 if (!isSupported)
1210 {
1211 return false;
1212 }
1213 }
1214
Kevin May42477c12020-03-26 13:34:14 +00001215 convLayers[index] = convLayer;
1216 }
1217 }
1218
1219 //
1220 // Set up Concat layer
1221 //
Finn Williamsb0331172020-10-08 14:33:13 +01001222 ConcatDescriptor concatDescriptor;
1223 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1224 concatDescriptor = ConcatDescriptor(weightsShape[0]);
Kevin May42477c12020-03-26 13:34:14 +00001225 for (unsigned int group = 0u; group < numGroups; ++group)
1226 {
1227 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1228 {
1229 auto index = group * channelMultiplier + m;
1230 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1231 concatDescriptor.SetConcatAxis(channelsIndex);
1232 }
1233 }
1234
1235 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001236 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1237 IsConcatSupported,
1238 data.m_Backends,
1239 isSupported,
1240 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1241 outputInfo,
1242 concatDescriptor);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001243
Kevin May42477c12020-03-26 13:34:14 +00001244 if (!isSupported)
1245 {
1246 return false;
1247 }
1248
1249 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1250 if (!concatLayer)
1251 {
1252 return Fail("%s: AddConcatLayer failed", __func__);
1253 }
1254
1255 for (unsigned int group = 0u; group < numGroups; ++group)
1256 {
1257 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1258 {
1259 auto index = group * channelMultiplier + m;
1260 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1261 }
1262 }
1263 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1264
Kevin Mayfcf2a152020-09-08 16:06:32 +01001265 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
Finn Williamsb0331172020-10-08 14:33:13 +01001266 data, nullptr, nullptr, activation);
Kevin May42477c12020-03-26 13:34:14 +00001267}
1268
1269template<typename HalPolicy,
1270 typename HalOperation = typename HalPolicy::Operation,
1271 typename HalModel = typename HalPolicy::Model>
1272bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1273{
1274 using HalOperand = typename HalPolicy::Operand;
1275 using HalOperandType = typename HalPolicy::OperandType;
1276
1277 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1278
1279 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1280 if (!input.IsValid())
1281 {
1282 return Fail("%s: Operation has an invalid input 0", __func__);
1283 }
1284
1285 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1286 if (!output)
1287 {
1288 return Fail("%s: Operation has an invalid output", __func__);
1289 }
1290
1291 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001292
1293 // Determine data type of input tensor
1294 HalOperandType inputType;
1295 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1296 {
1297 return Fail("%s: Operation has invalid inputs", __func__);
1298 }
1299
1300 InstanceNormalizationDescriptor desc;
1301
1302 // Read gamma, beta & epsilon
1303 if (inputType == HalOperandType::TENSOR_FLOAT16)
1304 {
1305 Half fp16Gamma;
1306 Half fp16Beta;
1307 Half fp16Epsilon;
1308
1309 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1310 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1311 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1312 {
1313 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1314 }
1315
1316 desc.m_Gamma = static_cast<float>(fp16Gamma);
1317 desc.m_Beta = static_cast<float>(fp16Beta);
1318 desc.m_Eps = static_cast<float>(fp16Epsilon);
1319 }
1320 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1321 {
1322 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1323 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1324 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1325 {
1326 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1327 }
1328 }
1329 else
1330 {
1331 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1332 }
1333
1334 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1335
1336 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001337 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1338 {
1339 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1340 IsInstanceNormalizationSupported,
1341 data.m_Backends,
1342 isSupported,
1343 input.GetTensorInfo(),
1344 outputInfo,
1345 desc);
1346 };
1347
1348 if(IsDynamicTensor(outputInfo))
1349 {
1350 isSupported = AreDynamicTensorsSupported();
1351 }
1352 else
1353 {
1354 validateFunc(outputInfo, isSupported);
1355 }
1356
Kevin May42477c12020-03-26 13:34:14 +00001357 if (!isSupported)
1358 {
1359 return false;
1360 }
1361
1362 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1363 input.Connect(layer->GetInputSlot(0));
1364
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001365 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001366}
1367
1368template<typename HalPolicy,
1369 typename HalOperation = typename HalPolicy::Operation,
1370 typename HalModel = typename HalPolicy::Model>
1371bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1372{
1373 using HalOperand = typename HalPolicy::Operand;
1374 using HalOperandType = typename HalPolicy::OperandType;
1375
1376 ALOGV("HalPolicy::ConvertLogSoftmax()");
1377
1378 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1379 if (!input.IsValid())
1380 {
1381 return Fail("%s: Failed to read input 0", __func__);
1382 }
1383
1384 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1385 if (!output)
1386 {
1387 return Fail("%s: Failed to read output", __func__);
1388 }
1389
1390 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001391
1392 // Determine data type of input tensor
1393 HalOperandType inputType;
1394 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1395 {
1396 return Fail("%s: Operation has invalid inputs", __func__);
1397 }
1398
1399 LogSoftmaxDescriptor descriptor;
1400
1401 // Read beta
1402 if (inputType == HalOperandType::TENSOR_FLOAT16)
1403 {
1404 Half fp16Beta;
1405 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1406 {
1407 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1408 }
1409
1410 descriptor.m_Beta = static_cast<float>(fp16Beta);
1411 }
1412 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1413 {
1414 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1415 {
1416 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1417 }
1418 }
1419 else
1420 {
1421 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1422 }
1423
1424 // Read axis
1425 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1426 {
1427 return Fail("%s: Failed to read input 2", __func__);
1428 }
1429
1430 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001431 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1432 {
1433 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1434 IsLogSoftmaxSupported,
1435 data.m_Backends,
1436 isSupported,
1437 input.GetTensorInfo(),
1438 outputInfo,
1439 descriptor);
1440 };
1441
1442 if(IsDynamicTensor(outputInfo))
1443 {
1444 isSupported = AreDynamicTensorsSupported();
1445 }
1446 else
1447 {
1448 validateFunc(outputInfo, isSupported);
1449 }
1450
Kevin May42477c12020-03-26 13:34:14 +00001451 if (!isSupported)
1452 {
1453 return false;
1454 }
1455
1456 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
1457 if (!layer)
1458 {
1459 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1460 }
1461
1462 input.Connect(layer->GetInputSlot(0));
1463
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001464 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001465}
1466
1467template<typename HalPolicy,
1468 typename HalOperation = typename HalPolicy::Operation,
1469 typename HalModel = typename HalPolicy::Model>
1470bool ConvertMaximum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1471{
1472 using HalOperand = typename HalPolicy::Operand;
1473
1474 ALOGV("HalPolicy::ConvertMaximum()");
1475
1476 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1477 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1478
1479 if (!input0.IsValid() || !input1.IsValid())
1480 {
1481 return Fail("%s: Operation has invalid inputs", __func__);
1482 }
1483
1484 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1485 if (!outputOperand)
1486 {
1487 return Fail("%s: Could not read output", __func__);
1488 }
1489
1490 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001491
1492 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001493 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
1494 {
1495 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1496 IsMaximumSupported,
1497 data.m_Backends,
1498 isSupported,
1499 input0.GetTensorInfo(),
1500 input1.GetTensorInfo(),
1501 outInfo);
1502 };
1503
1504 if(IsDynamicTensor(outInfo))
1505 {
1506 isSupported = AreDynamicTensorsSupported();
1507 }
1508 else
1509 {
1510 validateFunc(outInfo, isSupported);
1511 }
Kevin May42477c12020-03-26 13:34:14 +00001512
1513 if (!isSupported)
1514 {
1515 return false;
1516 }
1517
1518 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
1519 assert(layer != nullptr);
1520 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1521 if (!isReshapeSupported)
1522 {
1523 return false;
1524 }
1525
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001526 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001527}
1528
1529template<typename HalPolicy,
1530 typename HalOperation = typename HalPolicy::Operation,
1531 typename HalModel = typename HalPolicy::Model>
1532bool ConvertMinimum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1533{
1534 using HalOperand = typename HalPolicy::Operand;
1535
1536 ALOGV("HalPolicy::ConvertMinimum()");
1537
1538 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1539 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1540
1541 if (!input0.IsValid() || !input1.IsValid())
1542 {
1543 return Fail("%s: Operation has invalid inputs", __func__);
1544 }
1545
1546 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1547 if (!output)
1548 {
1549 return Fail("%s: Could not read output 0", __func__);
1550 }
1551
1552 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001553
1554 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001555 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1556 {
1557 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1558 IsMinimumSupported,
1559 data.m_Backends,
1560 isSupported,
1561 input0.GetTensorInfo(),
1562 input1.GetTensorInfo(),
1563 outputInfo);
1564 };
1565
1566 if(IsDynamicTensor(outputInfo))
1567 {
1568 isSupported = AreDynamicTensorsSupported();
1569 }
1570 else
1571 {
1572 validateFunc(outputInfo, isSupported);
1573 }
Kevin May42477c12020-03-26 13:34:14 +00001574
1575 if (!isSupported)
1576 {
1577 return false;
1578 }
1579
1580 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
1581 assert(layer != nullptr);
1582 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1583 if (!isReshapeSupported)
1584 {
1585 return false;
1586 }
1587
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001588 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001589}
1590
1591template<typename HalPolicy,
1592 typename HalOperation = typename HalPolicy::Operation,
1593 typename HalModel = typename HalPolicy::Model>
1594bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1595{
1596 using HalOperand = typename HalPolicy::Operand;
1597 using HalOperandType = typename HalPolicy::OperandType;
1598
1599 ALOGV("HalPolicy::ConvertPadV2()");
1600
1601 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1602 if (!input.IsValid())
1603 {
1604 return Fail("%s: Could not read input 0", __func__);
1605 }
1606
1607 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1608 if (!output)
1609 {
1610 return Fail("%s: Could not read output", __func__);
1611 }
1612
1613 const TensorInfo& inputInfo = input.GetTensorInfo();
1614 unsigned int rank = inputInfo.GetNumDimensions();
1615
1616 PadDescriptor descriptor;
1617 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1618 {
1619 return Fail("%s: Could not convert paddings", __func__);
1620 }
1621
1622 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001623
1624 // Determine type of padding value
1625 HalOperandType operandType0;
1626 HalOperandType operandType2;
1627
1628 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1629 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1630 {
1631 return Fail("%s: Operation has invalid inputs", __func__);
1632 }
1633
1634 // Read value to use for padding
1635 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1636 {
1637 Half f16PadValue;
1638 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1639 {
1640 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1641 }
1642
1643 descriptor.m_PadValue = f16PadValue;
1644 }
1645 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1646 {
1647 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1648 {
1649 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1650 }
1651 }
Teresa Charlind3381d52021-06-02 18:35:16 +01001652 else if (isQuantizedOperand(operandType0) && operandType2 == HalOperandType::INT32)
Kevin May42477c12020-03-26 13:34:14 +00001653 {
1654 int32_t intPadValue = 0;
1655 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1656 {
1657 return Fail("%s: Could not read input 2 (INT32)", __func__);
1658 }
1659 descriptor.m_PadValue = intPadValue;
1660 }
1661 else
1662 {
1663 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1664 }
1665
1666 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001667 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1668 {
1669 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1670 IsPadSupported,
1671 data.m_Backends,
1672 isSupported,
1673 inputInfo,
1674 outputInfo,
1675 descriptor);
1676 };
1677
1678 if(IsDynamicTensor(outputInfo))
1679 {
1680 isSupported = AreDynamicTensorsSupported();
1681 }
1682 else
1683 {
1684 validateFunc(outputInfo, isSupported);
1685 }
1686
Kevin May42477c12020-03-26 13:34:14 +00001687 if (!isSupported)
1688 {
1689 return false;
1690 }
1691
1692 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
1693 assert(layer != nullptr);
1694 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001695
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001696 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001697}
1698
1699template<typename HalPolicy,
1700 typename HalOperation = typename HalPolicy::Operation,
1701 typename HalModel = typename HalPolicy::Model>
1702bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1703{
1704 using HalOperand = typename HalPolicy::Operand;
1705
1706 ALOGV("HalPolicy::ConvertPrelu()");
1707
1708 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1709 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1710
1711 if (!input.IsValid() || !alpha.IsValid())
1712 {
1713 return Fail("%s: Operation has invalid inputs", __func__);
1714 }
1715
1716 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1717
1718 if (!output)
1719 {
1720 return Fail("%s: Could not read output", __func__);
1721 }
1722
1723 const TensorInfo& inputInfo = input.GetTensorInfo();
1724 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1725 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1726
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001727 bool isSupported = false;
1728 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001729 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001730 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1731 IsPreluSupported,
1732 data.m_Backends,
1733 isSupported,
1734 inputInfo,
1735 alphaInfo,
1736 outputInfo);
1737 };
1738
1739 if(IsDynamicTensor(outputInfo))
1740 {
1741 isSupported = AreDynamicTensorsSupported();
1742 }
1743 else
1744 {
1745 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001746 }
1747
Kevin May42477c12020-03-26 13:34:14 +00001748 if (!isSupported)
1749 {
1750 return false;
1751 }
1752
1753 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
1754
1755 if (!layer)
1756 {
1757 return Fail("%s: AddPreluLayer failed", __func__);
1758 }
1759
1760 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1761 if (!isReshapeSupported)
1762 {
1763 return false;
1764 }
1765
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001766 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001767}
1768
1769template<typename HalPolicy,
1770 typename HalOperation = typename HalPolicy::Operation,
1771 typename HalModel = typename HalPolicy::Model>
1772bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1773{
1774 using HalOperand = typename HalPolicy::Operand;
1775
1776 ALOGV("HalPolicy::ConvertQuantize()");
1777
1778 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1779 if (!input.IsValid())
1780 {
1781 return Fail("%s: Operation has invalid input", __func__);
1782 }
1783
1784 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1785 if (!outputOperand)
1786 {
1787 return Fail("%s: Operation has invalid outputs", __func__);
1788 }
1789
1790 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001791
1792 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001793 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1794 {
1795 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1796 IsQuantizeSupported,
1797 data.m_Backends,
1798 isSupported,
1799 input.GetTensorInfo(),
1800 outputInfo);
1801 };
1802
1803 if(IsDynamicTensor(outputInfo))
1804 {
1805 isSupported = AreDynamicTensorsSupported();
1806 }
1807 else
1808 {
1809 validateFunc(outputInfo, isSupported);
1810 }
1811
Kevin May42477c12020-03-26 13:34:14 +00001812 if (!isSupported)
1813 {
1814 return false;
1815 }
1816
1817 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
1818 assert(layer != nullptr);
1819 input.Connect(layer->GetInputSlot(0));
1820
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001821 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001822}
1823
1824template<typename HalPolicy,
1825 typename HalOperation = typename HalPolicy::Operation,
1826 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001827bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001828{
1829 using HalOperand = typename HalPolicy::Operand;
1830
Sadik Armagan813f2302020-05-19 14:10:30 +01001831 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001832
1833 //Inputs:
1834 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1835 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1836 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1837 if (!input.IsValid())
1838 {
1839 return Fail("%s: Could not read input 0: input", __func__);
1840 }
1841
1842 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1843 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1844 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1845 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1846 if (!previousCellStateIn.IsValid())
1847 {
1848 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1849 }
1850
1851 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1852 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1853 // is quantized with a fixed quantization range of -1, 127/128.
1854 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1855 if (!previousOutputIn.IsValid())
1856 {
1857 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1858 }
1859
1860 // Get the input tensors:
1861 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1862 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1863 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1864 const ConstTensorPin inputToInputWeightsPin =
1865 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1866
1867 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1868 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1869 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1870 const ConstTensorPin inputToForgetWeightsPin =
1871 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1872
1873 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1874 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1875 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1876 const ConstTensorPin inputToCellWeightsPin =
1877 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1878
1879 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1880 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1881 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1882 const ConstTensorPin inputToOutputWeightsPin =
1883 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1884
1885 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1886 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1887 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1888 const ConstTensorPin recurrentToInputWeightsPin =
1889 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1890
1891 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1892 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1893 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1894 const ConstTensorPin recurrentToForgetWeightsPin =
1895 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1896
1897 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1898 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1899 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1900 const ConstTensorPin recurrentToCellWeightsPin =
1901 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1902
1903 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1904 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1905 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1906 const ConstTensorPin recurrentToOutputWeightsPin =
1907 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1908
1909 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1910 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1911 // of input and weights scales and zeroPoint equal to 0.
1912 const ConstTensorPin inputGateBiasPin =
1913 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1914
1915 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1916 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1917 // of input and weights scales and zeroPoint equal to 0.
1918 const ConstTensorPin forgetGateBiasPin =
1919 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1920
1921 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1922 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1923 // and weights scales and zeroPoint equal to 0.
1924 const ConstTensorPin cellBiasPin =
1925 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1926
1927 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1928 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1929 // of input and weights scales and zeroPoint equal to 0.
1930 const ConstTensorPin outputGateBiasPin =
1931 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1932
1933 if (!inputToInputWeightsPin.IsValid() ||
1934 !inputToForgetWeightsPin.IsValid() ||
1935 !inputToCellWeightsPin.IsValid() ||
1936 !inputToOutputWeightsPin.IsValid() ||
1937 !recurrentToInputWeightsPin.IsValid() ||
1938 !recurrentToForgetWeightsPin.IsValid() ||
1939 !recurrentToCellWeightsPin.IsValid() ||
1940 !recurrentToOutputWeightsPin.IsValid() ||
1941 !inputGateBiasPin.IsValid() ||
1942 !forgetGateBiasPin.IsValid() ||
1943 !cellBiasPin.IsValid() ||
1944 !outputGateBiasPin.IsValid())
1945 {
1946 return Fail("%s: Operation has invalid tensor inputs", __func__);
1947 }
1948
1949 // Outputs:
1950 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1951 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1952 // of -2^4, 2^4 * 32767/32768.
1953 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1954 if (!cellStateOut)
1955 {
1956 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1957 }
1958
1959 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1960 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1961 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1962 if (!output)
1963 {
1964 return Fail("%s: Could not read output 1: output", __func__);
1965 }
1966
1967 // Inputs
1968 const TensorInfo& inputInfo = input.GetTensorInfo();
1969 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1970 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1971
1972 // Outputs
1973 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1974 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1975
1976 // Dynamic tensors currently not supported
1977 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1978 {
1979 return Fail("%s: Dynamic output tensors are not supported", __func__);
1980 }
1981
1982 QuantizedLstmInputParams params;
1983
1984 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1985 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1986 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1987 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1988 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1989 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1990 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1991 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1992 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1993 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1994 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1995 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1996
1997 QuantizedLstmInputParamsInfo paramsInfo;
1998 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1999 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2000 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2001 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2002 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2003 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2004 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2005 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2006 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2007 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2008 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2009 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2010
2011 bool isSupported = false;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002012 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2013 {
2014 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2015 IsQuantizedLstmSupported,
2016 data.m_Backends,
2017 isSupported,
2018 inputInfo,
2019 previousCellStateInInfo,
2020 previousOutputInInfo,
2021 cellStateOutInfo,
2022 outputInfo,
2023 paramsInfo);
2024 };
2025
2026 bool isDynamic = false;
2027 if (!IsDynamicTensor(cellStateOutInfo) &&
2028 !IsDynamicTensor(outputInfo))
2029 {
2030 validateFunc(outputInfo, isSupported);
2031 }
2032 else
2033 {
2034 isDynamic = true;
2035 isSupported = AreDynamicTensorsSupported();
2036 }
Kevin May42477c12020-03-26 13:34:14 +00002037
2038 if (!isSupported)
2039 {
2040 return false;
2041 }
2042
2043 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
2044 input.Connect(layer->GetInputSlot(0));
2045 previousCellStateIn.Connect(layer->GetInputSlot(1));
2046 previousOutputIn.Connect(layer->GetInputSlot(2));
2047
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002048 if (!isDynamic)
2049 {
2050 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2051 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
2052 }
2053 else
2054 {
2055 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2056 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002057 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002058 }
2059
Kevin May42477c12020-03-26 13:34:14 +00002060}
2061
2062template<typename HalPolicy,
2063 typename HalOperation = typename HalPolicy::Operation,
2064 typename HalModel = typename HalPolicy::Model>
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002065bool ConvertReduce(const HalOperation& operation,
2066 const HalModel& model,
2067 ConversionData& data,
2068 ReduceOperation reduceOperation)
2069{
2070 using HalOperand = typename HalPolicy::Operand;
2071 using HalOperandType = typename HalPolicy::OperandType;
2072
2073 armnn::ReduceDescriptor descriptor;
2074 descriptor.m_ReduceOperation = reduceOperation;
2075
2076 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2077 if (!input.IsValid())
2078 {
2079 return Fail("%s: Operation has invalid inputs", __func__);
2080 }
2081 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2082
2083 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2084 if (!output)
2085 {
2086 return Fail("%s: Could not read output 0", __func__);
2087 }
2088 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2089
2090 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2091 if (!axisOperand)
2092 {
2093 return Fail("%s: Could not read input 1", __func__);
2094 }
2095 std::vector<int32_t> axis;
2096 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2097 {
2098 return Fail("%s: Input 1 has invalid values", __func__);
2099 }
2100
2101 // Convert the axis to unsigned int and remove duplicates.
2102 unsigned int rank = inputInfo.GetNumDimensions();
2103 std::set<unsigned int> uniqueAxis;
2104 std::transform(axis.begin(), axis.end(),
2105 std::inserter(uniqueAxis, uniqueAxis.begin()),
2106 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2107 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
2108
2109 // Get the "keep dims" flag.
2110 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::BOOL, descriptor.m_KeepDims, model, data))
2111 {
2112 return Fail("%s: Could not read input 2", __func__);
2113 }
2114
2115 bool isSupported = false;
2116 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2117 {
2118 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2119 IsReduceSupported,
2120 data.m_Backends,
2121 isSupported,
2122 inputInfo,
2123 outputInfo,
2124 descriptor);
2125 };
2126
2127 if(!IsDynamicTensor(outputInfo))
2128 {
2129 validateFunc(outputInfo, isSupported);
2130 }
2131 else
2132 {
2133 isSupported = AreDynamicTensorsSupported();
2134 }
2135
2136 if (!isSupported)
2137 {
2138 return false;
2139 }
2140
2141 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
2142 assert(layer != nullptr);
2143 input.Connect(layer->GetInputSlot(0));
2144
2145 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
2146}
2147
2148template<typename HalPolicy,
2149 typename HalOperation = typename HalPolicy::Operation,
2150 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002151bool ConvertResize(const HalOperation& operation,
2152 const HalModel& model,
2153 ConversionData& data,
2154 ResizeMethod resizeMethod)
2155{
2156 using HalOperand = typename HalPolicy::Operand;
2157 using HalOperandType = typename HalPolicy::OperandType;
2158 ALOGV("HalPolicy::ConvertResize()");
2159 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
2160
2161 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2162 if (!input.IsValid())
2163 {
2164 return Fail("%s: Could not read input 0", __func__);
2165 }
2166
2167 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2168 if (!output)
2169 {
2170 return Fail("%s: Could not read output 0", __func__);
2171 }
2172
2173 const TensorInfo& inputInfo = input.GetTensorInfo();
2174 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2175
Kevin May42477c12020-03-26 13:34:14 +00002176 ResizeDescriptor descriptor;
2177 descriptor.m_Method = resizeMethod;
2178 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
2179
2180 HalOperandType operandType1;
2181 HalOperandType operandType2;
2182
2183 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
2184 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
2185 {
2186 return Fail("%s: Operation has invalid inputs", __func__);
2187 }
2188
2189 if (operandType1 != operandType2)
2190 {
2191 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
2192 }
2193
2194 if (operandType1 == HalOperandType::INT32)
2195 {
2196 // Case 1: resizing by shape
2197 int32_t targetWidth = 0;
2198 int32_t targetHeight = 0;
2199
2200 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
2201 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
2202 {
2203 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
2204 }
2205
2206 if (targetWidth < 0 || targetHeight < 0)
2207 {
2208 return Fail("%s: Operation has invalid inputs for resizing by shape. "
2209 "Target width/height cannot be < 0", __func__);
2210 }
2211
2212 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
2213 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
2214 }
2215 else if (operandType1 == HalOperandType::FLOAT32)
2216 {
2217 // Case 2: resizing by scale
2218 float widthScale = 1.0f;
2219 float heightScale = 1.0f;
2220
2221 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2222 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2223 {
2224 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2225 }
2226
2227 const TensorShape& inputShape = inputInfo.GetShape();
2228 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2229
2230 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2231 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2232
2233 descriptor.m_TargetWidth = std::floor(width * widthScale);
2234 descriptor.m_TargetHeight = std::floor(height * heightScale);
2235 }
2236 else if (operandType1 == HalOperandType::FLOAT16)
2237 {
2238 Half widthScale;
2239 Half heightScale;
2240
2241 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2242 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2243 {
2244 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2245 }
2246
2247 const TensorShape& inputShape = inputInfo.GetShape();
2248 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2249
2250 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2251 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2252
2253 descriptor.m_TargetWidth = std::floor(width * widthScale);
2254 descriptor.m_TargetHeight = std::floor(height * heightScale);
2255 }
2256 else
2257 {
2258 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2259 }
2260
David Monahanf057e6f2020-06-22 09:55:23 +01002261 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2262 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002263
Kevin May42477c12020-03-26 13:34:14 +00002264 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002265 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2266 {
2267 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2268 IsResizeSupported,
2269 data.m_Backends,
2270 isSupported,
2271 inputInfo,
2272 outputInfo,
2273 descriptor);
2274 };
2275
2276 if(IsDynamicTensor(outputInfo))
2277 {
2278 isSupported = AreDynamicTensorsSupported();
2279 }
2280 else
2281 {
2282 validateFunc(outputInfo, isSupported);
2283 }
Kevin May42477c12020-03-26 13:34:14 +00002284
2285 if (!isSupported)
2286 {
2287 return false;
2288 }
2289
2290 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Kevin May42477c12020-03-26 13:34:14 +00002291 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +00002292 input.Connect(layer->GetInputSlot(0));
2293
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002294 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002295}
2296
2297template<typename HalPolicy,
2298 typename HalOperation = typename HalPolicy::Operation,
2299 typename HalModel = typename HalPolicy::Model>
2300bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2301{
2302 using HalOperand = typename HalPolicy::Operand;
2303 using HalOperandType = typename HalPolicy::OperandType;
2304
2305 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2306
2307 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2308 if (!input.IsValid() )
2309 {
2310 return Fail("%s: Operation has invalid inputs", __func__);
2311 }
2312
2313 const TensorInfo& inputInfo = input.GetTensorInfo();
2314 unsigned int rank = inputInfo.GetNumDimensions();
2315 if (rank != 4)
2316 {
2317 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2318 }
2319
2320 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2321 if (!output)
2322 {
2323 return Fail("%s: Could not read output 0", __func__);
2324 }
2325
2326 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002327
2328 SpaceToDepthDescriptor desc;
2329
2330 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2331
2332 if (desc.m_BlockSize <= 1)
2333 {
2334 return Fail("%s: Block size must be at least 1 in all dimensions");
2335 }
2336
2337 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2338
2339 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002340 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2341 {
2342 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2343 IsSpaceToDepthSupported,
2344 data.m_Backends,
2345 isSupported,
2346 inputInfo,
2347 outputInfo,
2348 desc);
2349 };
2350
2351 if(IsDynamicTensor(outputInfo))
2352 {
2353 isSupported = AreDynamicTensorsSupported();
2354 }
2355 else
2356 {
2357 validateFunc(outputInfo, isSupported);
2358 }
2359
Kevin May42477c12020-03-26 13:34:14 +00002360 if (!isSupported)
2361 {
2362 return false;
2363 }
2364
2365 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
2366 assert(layer != nullptr);
2367 input.Connect(layer->GetInputSlot(0));
2368
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002369 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002370}
2371
2372template<typename HalPolicy,
2373 typename HalOperation = typename HalPolicy::Operation,
2374 typename HalModel = typename HalPolicy::Model>
2375bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2376{
2377 using HalOperand = typename HalPolicy::Operand;
2378 using HalOperandType = typename HalPolicy::OperandType;
2379
2380 ALOGV("HalPolicy::ConvertSoftmax()");
2381
2382 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2383 if (!input.IsValid())
2384 {
2385 return Fail("%s: Operation has invalid inputs", __func__);
2386 }
2387
2388 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2389 if (!outputOperand)
2390 {
2391 return Fail("%s: Operation has no outputs", __func__);
2392 }
2393
2394 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002395
2396 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002397 HalOperandType outputType = outputOperand->type;
2398
2399 // Read beta value
2400 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002401 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002402 Half value;
2403
2404 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2405 {
2406 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2407 }
2408
2409 desc.m_Beta = static_cast<float>(value);
2410 }
2411 else
2412 {
2413 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2414 {
2415 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2416 }
Kevin May42477c12020-03-26 13:34:14 +00002417 }
2418
2419 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002420 2,
2421 HalOperandType::INT32,
2422 desc.m_Axis,
2423 model,
2424 data))
Kevin May42477c12020-03-26 13:34:14 +00002425 {
2426 return Fail("%s: Operation has invalid inputs", __func__);
2427 }
2428
Kevin May42477c12020-03-26 13:34:14 +00002429 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002430 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2431 {
2432 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2433 IsSoftmaxSupported,
2434 data.m_Backends,
2435 isSupported,
2436 input.GetTensorInfo(),
2437 outputInfo,
2438 desc);
2439 };
2440
2441 if(IsDynamicTensor(outputInfo))
2442 {
2443 isSupported = AreDynamicTensorsSupported();
2444 }
2445 else
2446 {
2447 validateFunc(outputInfo, isSupported);
2448 }
2449
Kevin May42477c12020-03-26 13:34:14 +00002450 if (!isSupported)
2451 {
2452 return false;
2453 }
2454
2455 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
2456 assert(layer != nullptr);
2457 input.Connect(layer->GetInputSlot(0));
2458
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002459 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002460}
2461
2462template<typename HalPolicy,
2463 typename HalOperation = typename HalPolicy::Operation,
2464 typename HalModel = typename HalPolicy::Model>
2465bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2466{
2467 using HalOperand = typename HalPolicy::Operand;
2468 using HalOperandType = typename HalPolicy::OperandType;
2469
2470 ALOGV("HalPolicy::ConvertLstm()");
2471
2472 // Inputs:
2473 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2474 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2475 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2476 if (!input.IsValid())
2477 {
2478 return Fail("%s: Could not read input 0: input", __func__);
2479 }
2480 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2481 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2482 if (!outputStateIn.IsValid())
2483 {
2484 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2485 }
2486 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2487 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2488 if (!cellStateIn.IsValid())
2489 {
2490 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2491 }
2492
2493 // Get the mandatory input tensors:
2494 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2495 // [num_units, input_size].
2496 const ConstTensorPin inputToForgetWeightsPin =
2497 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2498 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2499 // [num_units, input_size].
2500 const ConstTensorPin inputToCellWeightsPin =
2501 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2502 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2503 // [num_units, input_size].
2504 const ConstTensorPin inputToOutputWeightsPin =
2505 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2506 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2507 // [num_units, output_size].
2508 const ConstTensorPin recurrentToForgetWeightsPin =
2509 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2510 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2511 // [num_units, output_size].
2512 const ConstTensorPin recurrentToCellWeightsPin =
2513 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2514 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2515 // [num_units, output_size].
2516 const ConstTensorPin recurrentToOutputWeightsPin =
2517 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2518 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2519 const ConstTensorPin forgetGateBiasPin =
2520 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2521 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2522 const ConstTensorPin cellBiasPin =
2523 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2524 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2525 const ConstTensorPin outputGateBiasPin =
2526 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2527
2528 if (!inputToForgetWeightsPin.IsValid() ||
2529 !inputToCellWeightsPin.IsValid() ||
2530 !inputToOutputWeightsPin.IsValid() ||
2531 !recurrentToForgetWeightsPin.IsValid() ||
2532 !recurrentToCellWeightsPin.IsValid() ||
2533 !recurrentToOutputWeightsPin.IsValid() ||
2534 !forgetGateBiasPin.IsValid() ||
2535 !cellBiasPin.IsValid() ||
2536 !outputGateBiasPin.IsValid())
2537 {
2538 return Fail("%s: Operation has invalid tensor inputs", __func__);
2539 }
2540
2541 // Get the optional input tensors:
2542 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2543 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2544 const ConstTensorPin inputToInputWeightsPin =
2545 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2546 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2547 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2548 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2549 const ConstTensorPin recurrentToInputWeightsPin =
2550 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2551 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2552 const ConstTensorPin cellToInputWeightsPin =
2553 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2554 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2555 const ConstTensorPin cellToForgetWeightsPin =
2556 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2557 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2558 const ConstTensorPin cellToOutputWeightsPin =
2559 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2560 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2561 const ConstTensorPin inputGateBiasPin =
2562 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2563 12,
2564 model,
2565 data,
2566 g_DontPermute,
2567 nullptr,
2568 true);
2569
2570 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2571 // [output_size, num_units].
2572 const ConstTensorPin projectionWeightsPin =
2573 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2574 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2575 const ConstTensorPin projectionBiasPin =
2576 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2577 17,
2578 model,
2579 data,
2580 g_DontPermute,
2581 nullptr,
2582 true);
2583
2584 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2585 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2586 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2587 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2588 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2589 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2590 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2591 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2592 {
2593 return Fail("%s: Operation has invalid tensor inputs", __func__);
2594 }
2595
2596 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2597 // 20: The activation function: A value indicating the activation function:
2598 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2599 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2600 // If set to 0.0 then clipping is disabled.
2601 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2602 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2603 ActivationFn activation;
2604 float cellClip;
2605 float projClip;
2606 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2607 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2608 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2609 {
2610 return Fail("%s: Operation has invalid scalar inputs", __func__);
2611 }
2612
2613 // Get the normalization tensors
2614 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2615 // Used to rescale normalized inputs to activation at input gate.
2616 const ConstTensorPin inputLayerNormWeightsPin
2617 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2618
2619 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2620 // Used to rescale normalized inputs to activation at forget gate.
2621 const ConstTensorPin forgetLayerNormWeightsPin =
2622 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2623 24,
2624 model,
2625 data,
2626 g_DontPermute,
2627 nullptr,
2628 true);
2629
2630 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2631 // Used to rescale normalized inputs to activation at cell gate.
2632 const ConstTensorPin cellLayerNormWeightsPin =
2633 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2634 25,
2635 model,
2636 data,
2637 g_DontPermute,
2638 nullptr,
2639 true);
2640
2641 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2642 // Used to rescale normalized inputs to activation at output gate.
2643 const ConstTensorPin outputLayerNormWeightsPin =
2644 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2645 26,
2646 model,
2647 data,
2648 g_DontPermute,
2649 nullptr,
2650 true);
2651
2652 // Outputs:
2653 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2654 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2655 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2656 if (!scratchBuffer)
2657 {
2658 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2659 }
2660 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2661 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2662 if (!outputStateOut)
2663 {
2664 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2665 }
2666 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2667 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2668 if (!cellStateOut)
2669 {
2670 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2671 }
2672 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2673 // effectively the same as the current “output state (out)” value.
2674 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2675 if (!output)
2676 {
2677 return Fail("%s: Could not read output 3: output", __func__);
2678 }
2679
2680 // set the params structure for the AddLstmLayer call
2681 LstmInputParams params;
2682 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2683 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2684 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2685 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2686 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2687 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2688 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2689 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2690 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2691 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2692 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2693 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2694 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2695 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2696 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2697 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2698 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2699 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2700 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2701 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2702 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2703
2704 // set the layer descriptor
2705 LstmDescriptor desc;
2706 desc.m_ActivationFunc = activation;
2707 desc.m_ClippingThresCell = cellClip;
2708 desc.m_ClippingThresProj = projClip;
2709 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2710 params.m_RecurrentToInputWeights == nullptr ||
2711 params.m_InputGateBias == nullptr);
2712 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2713 params.m_CellToOutputWeights != nullptr);
2714 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2715 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2716 params.m_ForgetLayerNormWeights != nullptr ||
2717 params.m_CellLayerNormWeights != nullptr ||
2718 params.m_OutputLayerNormWeights != nullptr);
2719
2720 // validate the optional input groups
2721 if (desc.m_CifgEnabled &&
2722 (params.m_InputToInputWeights != nullptr ||
2723 params.m_RecurrentToInputWeights != nullptr ||
2724 params.m_InputGateBias != nullptr))
2725 {
2726 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2727 " and input gate bias must be provided", __func__);
2728 }
2729
2730 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2731 {
2732 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2733 }
2734
2735 if (desc.m_PeepholeEnabled &&
2736 (params.m_CellToForgetWeights == nullptr ||
2737 params.m_CellToOutputWeights == nullptr ||
2738 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2739 {
2740 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2741 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2742 }
2743
2744 if (desc.m_LayerNormEnabled &&
2745 (params.m_ForgetLayerNormWeights == nullptr ||
2746 params.m_CellLayerNormWeights == nullptr ||
2747 params.m_OutputLayerNormWeights == nullptr ||
2748 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2749 {
2750 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2751 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2752 }
2753
2754 // Check if the layer is supported
2755 // Inputs
2756 const TensorInfo& inputInfo = input.GetTensorInfo();
2757 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2758 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2759
2760 // Outputs
2761 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2762 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2763 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2764 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2765
Kevin May42477c12020-03-26 13:34:14 +00002766 // Basic parameters
2767 LstmInputParamsInfo paramsInfo;
2768 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2769 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2770 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2771 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2772 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2773 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2774 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2775 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2776 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2777
2778 // Optional parameters
2779 if (!desc.m_CifgEnabled)
2780 {
2781 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2782 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2783 if (params.m_CellToInputWeights != nullptr)
2784 {
2785 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2786 }
2787 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2788 }
2789
2790 if (desc.m_ProjectionEnabled)
2791 {
2792 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2793 if (params.m_ProjectionBias != nullptr)
2794 {
2795 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2796 }
2797 }
2798
2799 if (desc.m_PeepholeEnabled)
2800 {
2801 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2802 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2803 }
2804
2805 if (desc.m_LayerNormEnabled)
2806 {
2807 if(!desc.m_CifgEnabled)
2808 {
2809 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2810 }
2811 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2812 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2813 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2814 }
2815
2816 bool isSupported = false;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002817 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2818 {
2819 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2820 IsLstmSupported,
2821 data.m_Backends,
2822 isSupported,
2823 inputInfo,
2824 outputStateInInfo,
2825 cellStateInInfo,
2826 scratchBufferInfo,
2827 outputStateOutInfo,
2828 cellStateOutInfo,
2829 outputInfo,
2830 desc,
2831 paramsInfo);
2832 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002833
Sadik Armagandbda4b72020-09-03 11:33:07 +01002834 bool isDynamic = false;
2835 if (!IsDynamicTensor(outputStateOutInfo) &&
2836 !IsDynamicTensor(scratchBufferInfo) &&
2837 !IsDynamicTensor(cellStateOutInfo) &&
2838 !IsDynamicTensor(outputInfo))
2839 {
2840 validateFunc(outputInfo, isSupported);
2841 }
2842 else
2843 {
2844 isDynamic = true;
2845 isSupported = AreDynamicTensorsSupported();
2846 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002847
Kevin May42477c12020-03-26 13:34:14 +00002848 if (!isSupported)
2849 {
2850 return false;
2851 }
2852
2853 // Add the layer
2854 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2855
2856 input.Connect(layer->GetInputSlot(0));
2857 outputStateIn.Connect(layer->GetInputSlot(1));
2858 cellStateIn.Connect(layer->GetInputSlot(2));
2859
Sadik Armagandbda4b72020-09-03 11:33:07 +01002860 if (!isDynamic)
2861 {
2862 return (
2863 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2864 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2865 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2866 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2867 }
2868 else
2869 {
2870 return (
2871 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2872 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2873 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2874 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002875 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002876 }
2877
Kevin May42477c12020-03-26 13:34:14 +00002878}
2879
2880template<typename HalPolicy,
2881 typename HalOperation = typename HalPolicy::Operation,
2882 typename HalModel = typename HalPolicy::Model>
2883bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2884{
2885 using HalOperand = typename HalPolicy::Operand;
2886 using HalOperandType = typename HalPolicy::OperandType;
2887
2888 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2889
2890 if (!input.IsValid())
2891 {
2892 return Fail("%s: Operation has invalid inputs", __func__);
2893 }
2894
2895 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2896
2897 if (!output)
2898 {
2899 return Fail("%s: Could not read output 0", __func__);
2900 }
2901
2902 const TensorInfo& inputInfo = input.GetTensorInfo();
2903 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002904
2905 // ArmNN does not currently support non-fixed weights or bias
2906 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2907 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2908
2909 if (weightsOperand == nullptr)
2910 {
2911 return Fail("%s: Operand is invalid", __func__);
2912 }
2913 TransposeConvolution2dDescriptor desc;
2914 desc.m_DataLayout = DataLayout::NHWC;
2915
2916 // Determine whether padding is implicit or explicit
2917 bool implicitPadding = operation.inputs.size() == 9;
2918
2919 if (implicitPadding )
2920 {
2921 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2922 }
2923 else
2924 {
2925 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2926 }
2927
2928 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2929 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2930 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2931
2932 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2933
2934 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2935 // We have to permute it to OIHW if the data layout is NCHW.
2936 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2937 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2938 model, data, OHWIToOIHW) :
2939 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2940
2941 // Bias is a 1D tensor
2942 const ConstTensorPin biasPin =
2943 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2944
2945 if (!weightsPin.IsValid())
2946 {
2947 return Fail("%s: Operation has invalid weights", __func__);
2948 }
2949
2950 if (!biasPin.IsValid())
2951 {
2952 return Fail("%s: Operation has invalid biases", __func__);
2953 }
2954
2955 ConstTensor weights = weightsPin.GetConstTensor();
2956 ConstTensor bias = biasPin.GetConstTensor();
2957 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2958
2959 ActivationFn activation;
2960
2961 if (implicitPadding)
2962 {
2963 int32_t strideX{0};
2964 int32_t strideY{0};
2965 int32_t padLeft{0};
2966 int32_t padRight{0};
2967 int32_t padTop{0};
2968 int32_t padBottom{0};
2969
2970 android::nn::PaddingScheme paddingScheme;
2971 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2972 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2973 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2974 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2975 {
2976 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2977 }
2978
2979 const uint32_t kernelX = weights.GetShape()[widthIndex];
2980 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00002981
Colm Donelan8f3d33e2020-07-06 15:41:43 +01002982 // If output shape has been specified as a parameter then extract it and make it available.
2983 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
2984 std::vector<int32_t> outputShape;
2985 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
2986 {
2987 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
2988 for (int dimension : outputShape)
2989 {
2990 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
2991 }
2992 desc.m_OutputShapeEnabled = true;
2993 }
2994
Finn Williams8fe50c62020-10-09 15:52:57 +01002995 uint32_t outputX;
2996 uint32_t outputY;
2997
2998 if (IsDynamicTensor(outputInfo))
2999 {
3000 if (outputShape.size() == 0)
3001 {
3002 return Fail("%s: Padding sizes cannot be inferred", __func__);
3003 }
3004
3005 outputX = outputShape[widthIndex];
3006 outputY = outputShape[heightIndex];
3007 }
3008 else
3009 {
3010 outputX = outputInfo.GetShape()[widthIndex];
3011 outputY = outputInfo.GetShape()[heightIndex];
3012 }
3013
3014 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
3015 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
3016
3017 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
3018 // but Arm NN only supports values >= 0
3019 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
3020 {
3021 return Fail("%s: Negative padding values are not supported", __func__);
3022 }
3023
Matthew Sloyan9b088d92020-09-14 15:12:55 +01003024 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
3025 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
3026 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
3027 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
3028 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
3029 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00003030 }
3031 else if (operation.inputs.size() == 11)
3032 {
3033 // explicit padding
3034 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
3035 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
3036 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
3037 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
3038 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
3039 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
3040 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
3041 {
3042 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
3043 }
3044 }
3045 else
3046 {
3047 return Fail("%s: Unsupported number of operation inputs", __func__);
3048 }
3049
3050 desc.m_BiasEnabled = true;
3051 Optional<TensorInfo> biases(bias.GetInfo());
3052
3053 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003054 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3055 {
3056 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3057 IsTransposeConvolution2dSupported,
3058 data.m_Backends,
3059 isSupported,
3060 inputInfo,
3061 outputInfo,
3062 desc,
3063 weights.GetInfo(),
3064 biases);
3065 };
3066
3067 if(IsDynamicTensor(outputInfo))
3068 {
3069 isSupported = AreDynamicTensorsSupported();
3070 }
3071 else
3072 {
3073 validateFunc(outputInfo, isSupported);
3074 }
Kevin May42477c12020-03-26 13:34:14 +00003075 if (!isSupported)
3076 {
3077 return false;
3078 }
3079
3080 IConnectableLayer* startLayer =
3081 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
3082 if (!startLayer)
3083 {
3084 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
3085 }
3086
Kevin May42477c12020-03-26 13:34:14 +00003087 input.Connect(startLayer->GetInputSlot(0));
3088
Kevin Mayfcf2a152020-09-08 16:06:32 +01003089 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
3090 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00003091}
3092
3093} // armnn_driver namespace