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