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