blob: eec87bf514b89ea983a0da42c40579d325b38347 [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
470 // Reinterpret weight data as [ H, W, I, M ]
471 TensorShape weightsShape({ weightsOperand->dimensions[1],
472 weightsOperand->dimensions[2],
473 inputInfo.GetShape()[channelsIndex],
474 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
475
476 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
477 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
478
479 const ConstTensorPin weightsPin =
480 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
481 1,
482 model,
483 data,
484 HWIMToMIHW,
485 &weightsShape);
486
487 // Bias is a 1D tensor
488 const ConstTensorPin biasPin =
489 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
490
491 if (!weightsPin.IsValid())
492 {
493 return Fail("%s: Operation has invalid weights", __func__);
494 }
495
496 if (!biasPin.IsValid())
497 {
498 return Fail("%s: Operation has invalid biases", __func__);
499 }
500
501 ConstTensor weights = weightsPin.GetConstTensor();
502 ConstTensor bias = biasPin.GetConstTensor();
503 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
504
505 ActivationFn activation;
506
507 if (implicitPadding)
508 {
509 android::nn::PaddingScheme paddingScheme;
510 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
511 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
512 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
513 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data) ||
514 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 9, desc, model, data))
515 {
516 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
517 }
518
519 const uint32_t kernelX = weights.GetShape()[3];
520 const uint32_t kernelY = weights.GetShape()[2];
521 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
522 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
523
524 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
525 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
526 }
527 else if (operation.inputs.size() >= 11)
528 {
529 // explicit padding
530 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
531 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
532 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
533 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
534 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
535 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
536 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data) ||
537 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 12, desc, model, data))
538 {
539 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
540 }
541 }
542 else
543 {
544 return Fail("%s: Unsupported number of operation inputs", __func__);
545 }
546
547 desc.m_BiasEnabled = true;
548 Optional<TensorInfo> biases(bias.GetInfo());
549
550 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100551 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
552 {
553 FORWARD_LAYER_SUPPORT_FUNC(__func__,
554 IsDepthwiseConvolutionSupported,
555 data.m_Backends,
556 isSupported,
557 inputInfo,
558 outputInfo,
559 desc,
560 weights.GetInfo(),
561 biases);
562 };
563
564 if(!IsDynamicTensor(outputInfo))
565 {
566 validateFunc(outputInfo, isSupported);
567 }
568 else
569 {
570 isSupported = AreDynamicTensorsSupported();
571 }
Kevin May42477c12020-03-26 13:34:14 +0000572
573 if (!isSupported)
574 {
575 return false;
576 }
577
578 IConnectableLayer* startLayer =
579 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
580
581 if (!startLayer)
582 {
583 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
584 }
585
Kevin May42477c12020-03-26 13:34:14 +0000586 input.Connect(startLayer->GetInputSlot(0));
587
Kevin Mayfcf2a152020-09-08 16:06:32 +0100588 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
589 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000590}
591
592template<typename HalPolicy,
593 typename HalOperation = typename HalPolicy::Operation,
594 typename HalModel = typename HalPolicy::Model>
595bool ConvertDequantize_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
596{
597 ALOGV("HalPolicy::ConvertDequantize()");
598
599 if (IsQSymmDequantizeForWeights<HalPolicy>(operation, model))
600 {
601 // NOTE: QSymm8 weights are dequantized internally by the driver,
602 // therefore this type of Dequantize is implicitly supported
603 return true;
604 }
605
606 return ::ConvertDequantize<HalPolicy>(operation, model, data);
607}
608
609template<typename HalPolicy,
610 typename HalOperation = typename HalPolicy::Operation,
611 typename HalModel = typename HalPolicy::Model>
612bool ConvertElementwiseUnary(const HalOperation& operation,
613 const HalModel& model,
614 ConversionData& data,
615 UnaryOperation unaryOperation)
616{
617 using HalOperand = typename HalPolicy::Operand;
618
619 ALOGV("HalPolicy::ConvertElementwiseUnary()");
620 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
621
622 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
623
624 if (!input.IsValid())
625 {
626 return Fail("%s: Operation has invalid input", __func__);
627 }
628
629 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
630 if (!output)
631 {
632 return Fail("%s: Could not read output 0", __func__);
633 }
634
635 const TensorInfo& inputInfo = input.GetTensorInfo();
636 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
637
Kevin May42477c12020-03-26 13:34:14 +0000638 ElementwiseUnaryDescriptor descriptor(unaryOperation);
639
640 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100641
642 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
643 {
644 FORWARD_LAYER_SUPPORT_FUNC(__func__,
645 IsElementwiseUnarySupported,
646 data.m_Backends,
647 isSupported,
648 inputInfo,
649 outputInfo,
650 descriptor);
651 };
652
653 if(!IsDynamicTensor(outputInfo))
654 {
655 validateFunc(outputInfo, isSupported);
656 }
657 else
658 {
659 isSupported = AreDynamicTensorsSupported();
660 }
Kevin May42477c12020-03-26 13:34:14 +0000661
662 if (!isSupported)
663 {
664 return false;
665 }
666
667 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
668 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +0000669 input.Connect(layer->GetInputSlot(0));
670
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100671 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000672}
673
674template<typename HalPolicy,
675 typename HalOperation = typename HalPolicy::Operation,
676 typename HalModel = typename HalPolicy::Model>
677bool ConvertExpandDims(const HalOperation& operation, const HalModel& model, ConversionData& data)
678{
679 using HalOperand = typename HalPolicy::Operand;
680 using HalOperandType = typename HalPolicy::OperandType;
681
682 ALOGV("HalPolicy::ConvertExpandDims()");
683
684 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
685
686 if (!input.IsValid())
687 {
688 return Fail("%s: Operation has invalid input", __func__);
689 }
690
691 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
692 if (!output)
693 {
694 return Fail("%s: Operation has invalid output", __func__);
695 }
696
697 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000698
699 int32_t axis;
700 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
701 {
702 return Fail("%s: failed to get axis input value", __func__);
703 }
704
705 TensorShape targetShape;
706
707 try
708 {
709 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
710 }
711 catch (const std::exception& e)
712 {
713 return Fail("%s: %s", __func__, e.what());
714 }
715
Kevin May42477c12020-03-26 13:34:14 +0000716 ReshapeDescriptor reshapeDescriptor;
717 reshapeDescriptor.m_TargetShape = targetShape;
718
719 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100720 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
721 {
722 FORWARD_LAYER_SUPPORT_FUNC(__func__,
723 IsReshapeSupported,
724 data.m_Backends,
725 isSupported,
726 input.GetTensorInfo(),
727 outputInfo,
728 reshapeDescriptor);
729 };
730
731 if(!IsDynamicTensor(outputInfo))
732 {
Nikhil Rajc5e0bb02021-04-02 10:02:16 +0100733 if (targetShape != outputInfo.GetShape())
734 {
735 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
736 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100737 validateFunc(outputInfo, isSupported);
738 }
739 else
740 {
741 isSupported = AreDynamicTensorsSupported();
742 }
Kevin May42477c12020-03-26 13:34:14 +0000743
744 if (!isSupported)
745 {
746 return false;
747 }
748
749 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
750 assert(layer != nullptr);
751 input.Connect(layer->GetInputSlot(0));
752
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100753 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000754}
755
756template<typename HalPolicy,
Teresa Charlinf931af92020-04-10 16:46:53 +0100757 typename HalOperation = typename HalPolicy::Operation,
758 typename HalModel = typename HalPolicy::Model>
759bool ConvertGather(const HalOperation& operation, const HalModel& model, ConversionData& data)
760{
761 using HalOperand = typename HalPolicy::Operand;
762 using HalOperandType = typename HalPolicy::OperandType;
763
764 ALOGV("HalPolicy::ConvertGather()");
765
766 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
767 if (!input.IsValid())
768 {
769 return Fail("%s: Operation has invalid input", __func__);
770 }
771 auto inputDimensions = input.GetTensorInfo().GetNumDimensions();
772
773 LayerInputHandle indices = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data);
774 if (!indices.IsValid())
775 {
776 return Fail("%s: Operation has invalid indices", __func__);
777 }
778 auto indicesDimensions = indices.GetTensorInfo().GetNumDimensions();
779
780 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
781 if (!output)
782 {
783 return Fail("%s: Operation has invalid output", __func__);
784 }
785 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
786 auto outputDimensions = outputInfo.GetNumDimensions();
Teresa Charlinf931af92020-04-10 16:46:53 +0100787 if (outputDimensions != inputDimensions + indicesDimensions - 1)
788 {
789 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 +0100790 __func__, outputDimensions, inputDimensions, indicesDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100791 }
792
793 int32_t axis;
794 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
795 {
796 return Fail("%s: Operation has invalid or unsupported axis operand", __func__);
797 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100798 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
Teresa Charlinf931af92020-04-10 16:46:53 +0100799 {
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100800 return Fail("%s: Operation has invalid axis: %d. It is out of bounds [-%d, %d))", __func__, axis,
801 inputDimensions, inputDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100802 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100803
804 GatherDescriptor desc;
805 desc.m_Axis = axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100806
807 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100808 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
809 {
810 FORWARD_LAYER_SUPPORT_FUNC(__func__,
811 IsGatherSupported,
812 data.m_Backends,
813 isSupported,
814 input.GetTensorInfo(),
815 indices.GetTensorInfo(),
816 outputInfo,
817 desc);
818 };
819
820 if(!IsDynamicTensor(outputInfo))
821 {
822 validateFunc(outputInfo, isSupported);
823 }
824 else
825 {
826 isSupported = AreDynamicTensorsSupported();
827 }
828
Teresa Charlinf931af92020-04-10 16:46:53 +0100829 if (!isSupported)
830 {
831 return false;
832 }
833
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100834 IConnectableLayer* layer = data.m_Network->AddGatherLayer(desc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100835 assert(layer != nullptr);
836 input.Connect(layer->GetInputSlot(0));
837 indices.Connect(layer->GetInputSlot(1));
838
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100839 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100840}
841
842template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000843 typename HalOperation = typename HalPolicy::Operation,
844 typename HalModel = typename HalPolicy::Model>
845bool ConvertGroupedConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
846{
847 using HalOperand = typename HalPolicy::Operand;
848 using HalOperandType = typename HalPolicy::OperandType;
849
850 ALOGV("HalPolicy::ConvertGroupedConv2d()");
851
852 //
853 // Parse data
854 //
855 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
856 if (!input.IsValid())
857 {
858 return Fail("%s: Operation has invalid inputs", __func__);
859 }
860 const TensorInfo& inputInfo = input.GetTensorInfo();
861
862 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
863 if (!output)
864 {
865 return Fail("%s: Could not read output 0", __func__);
866 }
Finn Williamsb0331172020-10-08 14:33:13 +0100867 TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000868
869 // Look ahead to determine data layout
870 DataLayout dataLayout = DataLayout::NHWC;
871 if (operation.inputs.size() == 12)
872 {
873 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
874 }
875 else
876 {
877 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
878 }
879
880 // NOTE:
881 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
882 // but Arm NN expects the filter's height and width indices to match the input's height and
883 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
884 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
885 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
886 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
887 model, data, ohwiToOihw) :
888 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
889 const ConstTensorPin biasesPin =
890 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
891 if (!weightsPin.IsValid() || !biasesPin.IsValid())
892 {
893 return Fail("%s: Operation has invalid inputs", __func__);
894 }
895
896 ConstTensor weights = weightsPin.GetConstTensor();
897 ConstTensor biases = biasesPin.GetConstTensor();
898 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
899
900 const TensorShape& inputShape = inputInfo.GetShape();
901 const TensorShape& outputShape = outputInfo.GetShape();
902 const TensorShape& weightsShape = weights.GetShape();
903 const TensorShape& biasesShape = biases.GetShape();
904
905 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
906 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
907 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
908 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
909
910 Convolution2dDescriptor desc;
911 desc.m_DataLayout = dataLayout;
912 desc.m_BiasEnabled = true;
913
914 int numGroups;
915 ActivationFn activation;
916
917 if (operation.inputs.size() == 12)
918 {
919 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
920 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
921 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
922 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
923 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
924 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
925 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
926 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
927 {
928 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
929 }
930
931 }
932 else if (operation.inputs.size() == 9)
933 {
934 android::nn::PaddingScheme paddingScheme;
935 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
936 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
937 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
938 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
939 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
940 {
941 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
942 }
943
944 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
945 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
946
947 const uint32_t kernelX = weightsShape[widthIndex];
948 const uint32_t kernelY = weightsShape[heightIndex];
949
950 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
951 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
952 }
953 else
954 {
955 return Fail("%s: Unsupported number of operation inputs", __func__);
956 }
957
Finn Williamsb0331172020-10-08 14:33:13 +0100958 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
959 const unsigned int outputChannels = weightsShape[0];
Kevin May42477c12020-03-26 13:34:14 +0000960
961 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
962 const unsigned int channelMultiplier = outputChannels / numGroups;
963
964 //
965 // Validate all relevant inputs
966 //
967 if (numGroups <= 0)
968 {
969 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
970 }
971
972 if (outputChannels % numGroups != 0u)
973 {
974 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
975 }
976
977 //
978 // Set up Splitter layer
979 //
980 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
981 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
982
983 TensorInfo splitterOutputInfo(4,
984 splitterDimSizes,
985 inputInfo.GetDataType(),
986 inputInfo.GetQuantizationScale(),
987 inputInfo.GetQuantizationOffset());
988
989 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
990
991 ViewsDescriptor splitterDesc(numGroups);
992 for (unsigned int group = 0u; group < numGroups; ++group)
993 {
994 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
995 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
996 {
997 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
998 }
999 }
1000
1001 bool isSupported = false;
1002 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1003 IsSplitterSupported,
1004 data.m_Backends,
1005 isSupported,
1006 inputInfo,
1007 splitterOutputInfos,
1008 splitterDesc);
1009 if (!isSupported)
1010 {
1011 return false;
1012 }
1013
1014 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
1015 if (!splitterLayer)
1016 {
1017 return Fail("%s: Failed to add SplitterLayer", __func__);
1018 }
1019
1020 input.Connect(splitterLayer->GetInputSlot(0));
1021 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
1022 {
1023 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
1024 }
1025
1026 //
1027 // Set up Convolution2d layers for each group
1028 //
1029
1030 // Set up group tensor shapes
1031 TensorShape groupInputShape(inputShape);
1032 groupInputShape[channelsIndex] = channelsPerGroup;
1033
Kevin May42477c12020-03-26 13:34:14 +00001034 TensorShape groupWeightsShape(weightsShape);
1035 groupWeightsShape[0] /= channelMultiplier * numGroups;
1036
1037 TensorShape groupBiasesShape({ 1 });
1038
1039 // Set up group tensor infos
1040 TensorInfo groupInputInfo(inputInfo);
1041 groupInputInfo.SetShape(groupInputShape);
1042
1043 const TensorInfo& weightsInfo = weights.GetInfo();
1044 TensorInfo groupWeightsInfo(weightsInfo);
1045 groupWeightsInfo.SetShape(groupWeightsShape);
1046
1047 const TensorInfo& biasesInfo = biases.GetInfo();
1048 TensorInfo groupBiasesInfo(biasesInfo);
1049 groupBiasesInfo.SetShape(groupBiasesShape);
1050
1051 TensorInfo groupOutputInfo(outputInfo);
Finn Williamsb0331172020-10-08 14:33:13 +01001052
1053 TensorShape groupOutputShape(outputShape);
1054 const bool isDynamic = IsDynamicTensor(outputInfo);
1055 if (!isDynamic)
1056 {
1057 groupOutputShape[channelsIndex] = 1;
1058 }
Kevin May42477c12020-03-26 13:34:14 +00001059 groupOutputInfo.SetShape(groupOutputShape);
1060
1061 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1062 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1063
1064 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1065 for (unsigned int group = 0u; group < numGroups; ++group)
1066 {
1067 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1068 {
1069 auto index = group * channelMultiplier + m;
1070
1071 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1072 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1073
1074 if (weightsInfo.HasPerAxisQuantization())
1075 {
1076 // Extract per-axis quantization scales for group weights
1077 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1078 groupWeightsInfo.SetQuantizationScales(
1079 std::vector<float>(weightsQuantScales.begin() + index,
1080 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1081
1082 // Extract per-axis quantization scales for group biases
1083 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1084 groupBiasesInfo.SetQuantizationScales(
1085 std::vector<float>(biasesQuantScales.begin() + index,
1086 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1087 }
1088
1089 // Extract weights and biases data for current group convolution
1090 ConstTensor groupWeights(groupWeightsInfo,
1091 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1092 weightsDataOffset));
1093 ConstTensor groupBiases(groupBiasesInfo,
1094 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1095 biasesDataOffset));
1096
1097 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001098 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1099 {
1100 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1101 IsConvolution2dSupported,
1102 data.m_Backends,
1103 isSupported,
1104 groupInputInfo,
1105 outputInfo,
1106 desc,
1107 groupWeightsInfo,
1108 Optional<TensorInfo>(groupBiasesInfo));
1109 };
1110
1111 if(!isDynamic)
1112 {
1113 validateFunc(groupOutputInfo, isSupported);
1114 }
1115 else
1116 {
1117 isSupported = AreDynamicTensorsSupported();
1118 }
1119
Kevin May42477c12020-03-26 13:34:14 +00001120 if (!isSupported)
1121 {
1122 return false;
1123 }
1124
1125 IConnectableLayer* convLayer =
1126 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1127 if (!convLayer)
1128 {
1129 return Fail("%s: AddConvolution2dLayer failed", __func__);
1130 }
1131
1132 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1133 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1134
Finn Williamsb0331172020-10-08 14:33:13 +01001135 if(isDynamic)
1136 {
1137 convLayer->GetOutputSlot(0).IsTensorInfoSet();
1138
1139 validateFunc(convLayer->GetOutputSlot(0).GetTensorInfo(), isSupported);
1140
1141 outputInfo = convLayer->GetOutputSlot(0).GetTensorInfo();
1142
1143 if (!isSupported)
1144 {
1145 return false;
1146 }
1147 }
1148
Kevin May42477c12020-03-26 13:34:14 +00001149 convLayers[index] = convLayer;
1150 }
1151 }
1152
1153 //
1154 // Set up Concat layer
1155 //
Finn Williamsb0331172020-10-08 14:33:13 +01001156 ConcatDescriptor concatDescriptor;
1157 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1158 concatDescriptor = ConcatDescriptor(weightsShape[0]);
Kevin May42477c12020-03-26 13:34:14 +00001159 for (unsigned int group = 0u; group < numGroups; ++group)
1160 {
1161 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1162 {
1163 auto index = group * channelMultiplier + m;
1164 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1165 concatDescriptor.SetConcatAxis(channelsIndex);
1166 }
1167 }
1168
1169 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001170 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1171 IsConcatSupported,
1172 data.m_Backends,
1173 isSupported,
1174 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1175 outputInfo,
1176 concatDescriptor);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001177
Kevin May42477c12020-03-26 13:34:14 +00001178 if (!isSupported)
1179 {
1180 return false;
1181 }
1182
1183 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1184 if (!concatLayer)
1185 {
1186 return Fail("%s: AddConcatLayer failed", __func__);
1187 }
1188
1189 for (unsigned int group = 0u; group < numGroups; ++group)
1190 {
1191 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1192 {
1193 auto index = group * channelMultiplier + m;
1194 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1195 }
1196 }
1197 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1198
Kevin Mayfcf2a152020-09-08 16:06:32 +01001199 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
Finn Williamsb0331172020-10-08 14:33:13 +01001200 data, nullptr, nullptr, activation);
Kevin May42477c12020-03-26 13:34:14 +00001201}
1202
1203template<typename HalPolicy,
1204 typename HalOperation = typename HalPolicy::Operation,
1205 typename HalModel = typename HalPolicy::Model>
1206bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1207{
1208 using HalOperand = typename HalPolicy::Operand;
1209 using HalOperandType = typename HalPolicy::OperandType;
1210
1211 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1212
1213 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1214 if (!input.IsValid())
1215 {
1216 return Fail("%s: Operation has an invalid input 0", __func__);
1217 }
1218
1219 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1220 if (!output)
1221 {
1222 return Fail("%s: Operation has an invalid output", __func__);
1223 }
1224
1225 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001226
1227 // Determine data type of input tensor
1228 HalOperandType inputType;
1229 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1230 {
1231 return Fail("%s: Operation has invalid inputs", __func__);
1232 }
1233
1234 InstanceNormalizationDescriptor desc;
1235
1236 // Read gamma, beta & epsilon
1237 if (inputType == HalOperandType::TENSOR_FLOAT16)
1238 {
1239 Half fp16Gamma;
1240 Half fp16Beta;
1241 Half fp16Epsilon;
1242
1243 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1244 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1245 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1246 {
1247 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1248 }
1249
1250 desc.m_Gamma = static_cast<float>(fp16Gamma);
1251 desc.m_Beta = static_cast<float>(fp16Beta);
1252 desc.m_Eps = static_cast<float>(fp16Epsilon);
1253 }
1254 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1255 {
1256 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1257 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1258 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1259 {
1260 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1261 }
1262 }
1263 else
1264 {
1265 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1266 }
1267
1268 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1269
1270 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001271 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1272 {
1273 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1274 IsInstanceNormalizationSupported,
1275 data.m_Backends,
1276 isSupported,
1277 input.GetTensorInfo(),
1278 outputInfo,
1279 desc);
1280 };
1281
1282 if(IsDynamicTensor(outputInfo))
1283 {
1284 isSupported = AreDynamicTensorsSupported();
1285 }
1286 else
1287 {
1288 validateFunc(outputInfo, isSupported);
1289 }
1290
Kevin May42477c12020-03-26 13:34:14 +00001291 if (!isSupported)
1292 {
1293 return false;
1294 }
1295
1296 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1297 input.Connect(layer->GetInputSlot(0));
1298
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001299 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001300}
1301
1302template<typename HalPolicy,
1303 typename HalOperation = typename HalPolicy::Operation,
1304 typename HalModel = typename HalPolicy::Model>
1305bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1306{
1307 using HalOperand = typename HalPolicy::Operand;
1308 using HalOperandType = typename HalPolicy::OperandType;
1309
1310 ALOGV("HalPolicy::ConvertLogSoftmax()");
1311
1312 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1313 if (!input.IsValid())
1314 {
1315 return Fail("%s: Failed to read input 0", __func__);
1316 }
1317
1318 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1319 if (!output)
1320 {
1321 return Fail("%s: Failed to read output", __func__);
1322 }
1323
1324 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001325
1326 // Determine data type of input tensor
1327 HalOperandType inputType;
1328 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1329 {
1330 return Fail("%s: Operation has invalid inputs", __func__);
1331 }
1332
1333 LogSoftmaxDescriptor descriptor;
1334
1335 // Read beta
1336 if (inputType == HalOperandType::TENSOR_FLOAT16)
1337 {
1338 Half fp16Beta;
1339 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1340 {
1341 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1342 }
1343
1344 descriptor.m_Beta = static_cast<float>(fp16Beta);
1345 }
1346 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1347 {
1348 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1349 {
1350 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1351 }
1352 }
1353 else
1354 {
1355 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1356 }
1357
1358 // Read axis
1359 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1360 {
1361 return Fail("%s: Failed to read input 2", __func__);
1362 }
1363
1364 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001365 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1366 {
1367 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1368 IsLogSoftmaxSupported,
1369 data.m_Backends,
1370 isSupported,
1371 input.GetTensorInfo(),
1372 outputInfo,
1373 descriptor);
1374 };
1375
1376 if(IsDynamicTensor(outputInfo))
1377 {
1378 isSupported = AreDynamicTensorsSupported();
1379 }
1380 else
1381 {
1382 validateFunc(outputInfo, isSupported);
1383 }
1384
Kevin May42477c12020-03-26 13:34:14 +00001385 if (!isSupported)
1386 {
1387 return false;
1388 }
1389
1390 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
1391 if (!layer)
1392 {
1393 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1394 }
1395
1396 input.Connect(layer->GetInputSlot(0));
1397
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001398 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001399}
1400
1401template<typename HalPolicy,
1402 typename HalOperation = typename HalPolicy::Operation,
1403 typename HalModel = typename HalPolicy::Model>
1404bool ConvertMaximum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1405{
1406 using HalOperand = typename HalPolicy::Operand;
1407
1408 ALOGV("HalPolicy::ConvertMaximum()");
1409
1410 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1411 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1412
1413 if (!input0.IsValid() || !input1.IsValid())
1414 {
1415 return Fail("%s: Operation has invalid inputs", __func__);
1416 }
1417
1418 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1419 if (!outputOperand)
1420 {
1421 return Fail("%s: Could not read output", __func__);
1422 }
1423
1424 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001425
1426 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001427 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
1428 {
1429 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1430 IsMaximumSupported,
1431 data.m_Backends,
1432 isSupported,
1433 input0.GetTensorInfo(),
1434 input1.GetTensorInfo(),
1435 outInfo);
1436 };
1437
1438 if(IsDynamicTensor(outInfo))
1439 {
1440 isSupported = AreDynamicTensorsSupported();
1441 }
1442 else
1443 {
1444 validateFunc(outInfo, isSupported);
1445 }
Kevin May42477c12020-03-26 13:34:14 +00001446
1447 if (!isSupported)
1448 {
1449 return false;
1450 }
1451
1452 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
1453 assert(layer != nullptr);
1454 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1455 if (!isReshapeSupported)
1456 {
1457 return false;
1458 }
1459
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001460 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001461}
1462
1463template<typename HalPolicy,
1464 typename HalOperation = typename HalPolicy::Operation,
1465 typename HalModel = typename HalPolicy::Model>
1466bool ConvertMinimum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1467{
1468 using HalOperand = typename HalPolicy::Operand;
1469
1470 ALOGV("HalPolicy::ConvertMinimum()");
1471
1472 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1473 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1474
1475 if (!input0.IsValid() || !input1.IsValid())
1476 {
1477 return Fail("%s: Operation has invalid inputs", __func__);
1478 }
1479
1480 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1481 if (!output)
1482 {
1483 return Fail("%s: Could not read output 0", __func__);
1484 }
1485
1486 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001487
1488 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001489 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1490 {
1491 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1492 IsMinimumSupported,
1493 data.m_Backends,
1494 isSupported,
1495 input0.GetTensorInfo(),
1496 input1.GetTensorInfo(),
1497 outputInfo);
1498 };
1499
1500 if(IsDynamicTensor(outputInfo))
1501 {
1502 isSupported = AreDynamicTensorsSupported();
1503 }
1504 else
1505 {
1506 validateFunc(outputInfo, isSupported);
1507 }
Kevin May42477c12020-03-26 13:34:14 +00001508
1509 if (!isSupported)
1510 {
1511 return false;
1512 }
1513
1514 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
1515 assert(layer != nullptr);
1516 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1517 if (!isReshapeSupported)
1518 {
1519 return false;
1520 }
1521
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001522 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001523}
1524
1525template<typename HalPolicy,
1526 typename HalOperation = typename HalPolicy::Operation,
1527 typename HalModel = typename HalPolicy::Model>
1528bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1529{
1530 using HalOperand = typename HalPolicy::Operand;
1531 using HalOperandType = typename HalPolicy::OperandType;
1532
1533 ALOGV("HalPolicy::ConvertPadV2()");
1534
1535 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1536 if (!input.IsValid())
1537 {
1538 return Fail("%s: Could not read input 0", __func__);
1539 }
1540
1541 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1542 if (!output)
1543 {
1544 return Fail("%s: Could not read output", __func__);
1545 }
1546
1547 const TensorInfo& inputInfo = input.GetTensorInfo();
1548 unsigned int rank = inputInfo.GetNumDimensions();
1549
1550 PadDescriptor descriptor;
1551 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1552 {
1553 return Fail("%s: Could not convert paddings", __func__);
1554 }
1555
1556 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001557
1558 // Determine type of padding value
1559 HalOperandType operandType0;
1560 HalOperandType operandType2;
1561
1562 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1563 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1564 {
1565 return Fail("%s: Operation has invalid inputs", __func__);
1566 }
1567
1568 // Read value to use for padding
1569 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1570 {
1571 Half f16PadValue;
1572 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1573 {
1574 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1575 }
1576
1577 descriptor.m_PadValue = f16PadValue;
1578 }
1579 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1580 {
1581 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1582 {
1583 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1584 }
1585 }
1586 else if (operandType0 == HalOperandType::TENSOR_QUANT8_ASYMM && operandType2 == HalOperandType::INT32)
1587 {
1588 int32_t intPadValue = 0;
1589 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1590 {
1591 return Fail("%s: Could not read input 2 (INT32)", __func__);
1592 }
1593 descriptor.m_PadValue = intPadValue;
1594 }
1595 else
1596 {
1597 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1598 }
1599
1600 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001601 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1602 {
1603 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1604 IsPadSupported,
1605 data.m_Backends,
1606 isSupported,
1607 inputInfo,
1608 outputInfo,
1609 descriptor);
1610 };
1611
1612 if(IsDynamicTensor(outputInfo))
1613 {
1614 isSupported = AreDynamicTensorsSupported();
1615 }
1616 else
1617 {
1618 validateFunc(outputInfo, isSupported);
1619 }
1620
Kevin May42477c12020-03-26 13:34:14 +00001621 if (!isSupported)
1622 {
1623 return false;
1624 }
1625
1626 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
1627 assert(layer != nullptr);
1628 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001629
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001630 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001631}
1632
1633template<typename HalPolicy,
1634 typename HalOperation = typename HalPolicy::Operation,
1635 typename HalModel = typename HalPolicy::Model>
1636bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1637{
1638 using HalOperand = typename HalPolicy::Operand;
1639
1640 ALOGV("HalPolicy::ConvertPrelu()");
1641
1642 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1643 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1644
1645 if (!input.IsValid() || !alpha.IsValid())
1646 {
1647 return Fail("%s: Operation has invalid inputs", __func__);
1648 }
1649
1650 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1651
1652 if (!output)
1653 {
1654 return Fail("%s: Could not read output", __func__);
1655 }
1656
1657 const TensorInfo& inputInfo = input.GetTensorInfo();
1658 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1659 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1660
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001661 bool isSupported = false;
1662 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001663 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001664 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1665 IsPreluSupported,
1666 data.m_Backends,
1667 isSupported,
1668 inputInfo,
1669 alphaInfo,
1670 outputInfo);
1671 };
1672
1673 if(IsDynamicTensor(outputInfo))
1674 {
1675 isSupported = AreDynamicTensorsSupported();
1676 }
1677 else
1678 {
1679 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001680 }
1681
Kevin May42477c12020-03-26 13:34:14 +00001682 if (!isSupported)
1683 {
1684 return false;
1685 }
1686
1687 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
1688
1689 if (!layer)
1690 {
1691 return Fail("%s: AddPreluLayer failed", __func__);
1692 }
1693
1694 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1695 if (!isReshapeSupported)
1696 {
1697 return false;
1698 }
1699
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001700 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001701}
1702
1703template<typename HalPolicy,
1704 typename HalOperation = typename HalPolicy::Operation,
1705 typename HalModel = typename HalPolicy::Model>
1706bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1707{
1708 using HalOperand = typename HalPolicy::Operand;
1709
1710 ALOGV("HalPolicy::ConvertQuantize()");
1711
1712 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1713 if (!input.IsValid())
1714 {
1715 return Fail("%s: Operation has invalid input", __func__);
1716 }
1717
1718 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1719 if (!outputOperand)
1720 {
1721 return Fail("%s: Operation has invalid outputs", __func__);
1722 }
1723
1724 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001725
1726 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001727 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1728 {
1729 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1730 IsQuantizeSupported,
1731 data.m_Backends,
1732 isSupported,
1733 input.GetTensorInfo(),
1734 outputInfo);
1735 };
1736
1737 if(IsDynamicTensor(outputInfo))
1738 {
1739 isSupported = AreDynamicTensorsSupported();
1740 }
1741 else
1742 {
1743 validateFunc(outputInfo, isSupported);
1744 }
1745
Kevin May42477c12020-03-26 13:34:14 +00001746 if (!isSupported)
1747 {
1748 return false;
1749 }
1750
1751 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
1752 assert(layer != nullptr);
1753 input.Connect(layer->GetInputSlot(0));
1754
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001755 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001756}
1757
1758template<typename HalPolicy,
1759 typename HalOperation = typename HalPolicy::Operation,
1760 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001761bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001762{
1763 using HalOperand = typename HalPolicy::Operand;
1764
Sadik Armagan813f2302020-05-19 14:10:30 +01001765 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001766
1767 //Inputs:
1768 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1769 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1770 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1771 if (!input.IsValid())
1772 {
1773 return Fail("%s: Could not read input 0: input", __func__);
1774 }
1775
1776 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1777 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1778 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1779 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1780 if (!previousCellStateIn.IsValid())
1781 {
1782 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1783 }
1784
1785 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1786 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1787 // is quantized with a fixed quantization range of -1, 127/128.
1788 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1789 if (!previousOutputIn.IsValid())
1790 {
1791 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1792 }
1793
1794 // Get the input tensors:
1795 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1796 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1797 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1798 const ConstTensorPin inputToInputWeightsPin =
1799 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1800
1801 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1802 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1803 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1804 const ConstTensorPin inputToForgetWeightsPin =
1805 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1806
1807 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1808 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1809 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1810 const ConstTensorPin inputToCellWeightsPin =
1811 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1812
1813 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1814 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1815 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1816 const ConstTensorPin inputToOutputWeightsPin =
1817 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1818
1819 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1820 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1821 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1822 const ConstTensorPin recurrentToInputWeightsPin =
1823 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1824
1825 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1826 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1827 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1828 const ConstTensorPin recurrentToForgetWeightsPin =
1829 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1830
1831 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1832 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1833 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1834 const ConstTensorPin recurrentToCellWeightsPin =
1835 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1836
1837 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1838 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1839 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1840 const ConstTensorPin recurrentToOutputWeightsPin =
1841 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1842
1843 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1844 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1845 // of input and weights scales and zeroPoint equal to 0.
1846 const ConstTensorPin inputGateBiasPin =
1847 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1848
1849 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1850 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1851 // of input and weights scales and zeroPoint equal to 0.
1852 const ConstTensorPin forgetGateBiasPin =
1853 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1854
1855 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1856 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1857 // and weights scales and zeroPoint equal to 0.
1858 const ConstTensorPin cellBiasPin =
1859 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1860
1861 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1862 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1863 // of input and weights scales and zeroPoint equal to 0.
1864 const ConstTensorPin outputGateBiasPin =
1865 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1866
1867 if (!inputToInputWeightsPin.IsValid() ||
1868 !inputToForgetWeightsPin.IsValid() ||
1869 !inputToCellWeightsPin.IsValid() ||
1870 !inputToOutputWeightsPin.IsValid() ||
1871 !recurrentToInputWeightsPin.IsValid() ||
1872 !recurrentToForgetWeightsPin.IsValid() ||
1873 !recurrentToCellWeightsPin.IsValid() ||
1874 !recurrentToOutputWeightsPin.IsValid() ||
1875 !inputGateBiasPin.IsValid() ||
1876 !forgetGateBiasPin.IsValid() ||
1877 !cellBiasPin.IsValid() ||
1878 !outputGateBiasPin.IsValid())
1879 {
1880 return Fail("%s: Operation has invalid tensor inputs", __func__);
1881 }
1882
1883 // Outputs:
1884 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1885 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1886 // of -2^4, 2^4 * 32767/32768.
1887 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1888 if (!cellStateOut)
1889 {
1890 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1891 }
1892
1893 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1894 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1895 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1896 if (!output)
1897 {
1898 return Fail("%s: Could not read output 1: output", __func__);
1899 }
1900
1901 // Inputs
1902 const TensorInfo& inputInfo = input.GetTensorInfo();
1903 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1904 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1905
1906 // Outputs
1907 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1908 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1909
1910 // Dynamic tensors currently not supported
1911 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1912 {
1913 return Fail("%s: Dynamic output tensors are not supported", __func__);
1914 }
1915
1916 QuantizedLstmInputParams params;
1917
1918 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1919 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1920 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1921 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1922 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1923 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1924 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1925 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1926 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1927 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1928 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1929 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1930
1931 QuantizedLstmInputParamsInfo paramsInfo;
1932 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1933 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1934 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1935 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1936 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1937 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1938 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1939 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1940 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1941 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1942 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1943 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1944
1945 bool isSupported = false;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001946 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1947 {
1948 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1949 IsQuantizedLstmSupported,
1950 data.m_Backends,
1951 isSupported,
1952 inputInfo,
1953 previousCellStateInInfo,
1954 previousOutputInInfo,
1955 cellStateOutInfo,
1956 outputInfo,
1957 paramsInfo);
1958 };
1959
1960 bool isDynamic = false;
1961 if (!IsDynamicTensor(cellStateOutInfo) &&
1962 !IsDynamicTensor(outputInfo))
1963 {
1964 validateFunc(outputInfo, isSupported);
1965 }
1966 else
1967 {
1968 isDynamic = true;
1969 isSupported = AreDynamicTensorsSupported();
1970 }
Kevin May42477c12020-03-26 13:34:14 +00001971
1972 if (!isSupported)
1973 {
1974 return false;
1975 }
1976
1977 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1978 input.Connect(layer->GetInputSlot(0));
1979 previousCellStateIn.Connect(layer->GetInputSlot(1));
1980 previousOutputIn.Connect(layer->GetInputSlot(2));
1981
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001982 if (!isDynamic)
1983 {
1984 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1985 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
1986 }
1987 else
1988 {
1989 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1990 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01001991 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001992 }
1993
Kevin May42477c12020-03-26 13:34:14 +00001994}
1995
1996template<typename HalPolicy,
1997 typename HalOperation = typename HalPolicy::Operation,
1998 typename HalModel = typename HalPolicy::Model>
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00001999bool ConvertReduce(const HalOperation& operation,
2000 const HalModel& model,
2001 ConversionData& data,
2002 ReduceOperation reduceOperation)
2003{
2004 using HalOperand = typename HalPolicy::Operand;
2005 using HalOperandType = typename HalPolicy::OperandType;
2006
2007 armnn::ReduceDescriptor descriptor;
2008 descriptor.m_ReduceOperation = reduceOperation;
2009
2010 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2011 if (!input.IsValid())
2012 {
2013 return Fail("%s: Operation has invalid inputs", __func__);
2014 }
2015 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2016
2017 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2018 if (!output)
2019 {
2020 return Fail("%s: Could not read output 0", __func__);
2021 }
2022 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2023
2024 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2025 if (!axisOperand)
2026 {
2027 return Fail("%s: Could not read input 1", __func__);
2028 }
2029 std::vector<int32_t> axis;
2030 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2031 {
2032 return Fail("%s: Input 1 has invalid values", __func__);
2033 }
2034
2035 // Convert the axis to unsigned int and remove duplicates.
2036 unsigned int rank = inputInfo.GetNumDimensions();
2037 std::set<unsigned int> uniqueAxis;
2038 std::transform(axis.begin(), axis.end(),
2039 std::inserter(uniqueAxis, uniqueAxis.begin()),
2040 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2041 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
2042
2043 // Get the "keep dims" flag.
2044 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::BOOL, descriptor.m_KeepDims, model, data))
2045 {
2046 return Fail("%s: Could not read input 2", __func__);
2047 }
2048
2049 bool isSupported = false;
2050 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2051 {
2052 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2053 IsReduceSupported,
2054 data.m_Backends,
2055 isSupported,
2056 inputInfo,
2057 outputInfo,
2058 descriptor);
2059 };
2060
2061 if(!IsDynamicTensor(outputInfo))
2062 {
2063 validateFunc(outputInfo, isSupported);
2064 }
2065 else
2066 {
2067 isSupported = AreDynamicTensorsSupported();
2068 }
2069
2070 if (!isSupported)
2071 {
2072 return false;
2073 }
2074
2075 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
2076 assert(layer != nullptr);
2077 input.Connect(layer->GetInputSlot(0));
2078
2079 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
2080}
2081
2082template<typename HalPolicy,
2083 typename HalOperation = typename HalPolicy::Operation,
2084 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002085bool ConvertResize(const HalOperation& operation,
2086 const HalModel& model,
2087 ConversionData& data,
2088 ResizeMethod resizeMethod)
2089{
2090 using HalOperand = typename HalPolicy::Operand;
2091 using HalOperandType = typename HalPolicy::OperandType;
2092 ALOGV("HalPolicy::ConvertResize()");
2093 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
2094
2095 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2096 if (!input.IsValid())
2097 {
2098 return Fail("%s: Could not read input 0", __func__);
2099 }
2100
2101 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2102 if (!output)
2103 {
2104 return Fail("%s: Could not read output 0", __func__);
2105 }
2106
2107 const TensorInfo& inputInfo = input.GetTensorInfo();
2108 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2109
Kevin May42477c12020-03-26 13:34:14 +00002110 ResizeDescriptor descriptor;
2111 descriptor.m_Method = resizeMethod;
2112 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
2113
2114 HalOperandType operandType1;
2115 HalOperandType operandType2;
2116
2117 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
2118 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
2119 {
2120 return Fail("%s: Operation has invalid inputs", __func__);
2121 }
2122
2123 if (operandType1 != operandType2)
2124 {
2125 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
2126 }
2127
2128 if (operandType1 == HalOperandType::INT32)
2129 {
2130 // Case 1: resizing by shape
2131 int32_t targetWidth = 0;
2132 int32_t targetHeight = 0;
2133
2134 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
2135 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
2136 {
2137 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
2138 }
2139
2140 if (targetWidth < 0 || targetHeight < 0)
2141 {
2142 return Fail("%s: Operation has invalid inputs for resizing by shape. "
2143 "Target width/height cannot be < 0", __func__);
2144 }
2145
2146 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
2147 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
2148 }
2149 else if (operandType1 == HalOperandType::FLOAT32)
2150 {
2151 // Case 2: resizing by scale
2152 float widthScale = 1.0f;
2153 float heightScale = 1.0f;
2154
2155 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2156 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2157 {
2158 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2159 }
2160
2161 const TensorShape& inputShape = inputInfo.GetShape();
2162 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2163
2164 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2165 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2166
2167 descriptor.m_TargetWidth = std::floor(width * widthScale);
2168 descriptor.m_TargetHeight = std::floor(height * heightScale);
2169 }
2170 else if (operandType1 == HalOperandType::FLOAT16)
2171 {
2172 Half widthScale;
2173 Half heightScale;
2174
2175 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2176 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2177 {
2178 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2179 }
2180
2181 const TensorShape& inputShape = inputInfo.GetShape();
2182 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2183
2184 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2185 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2186
2187 descriptor.m_TargetWidth = std::floor(width * widthScale);
2188 descriptor.m_TargetHeight = std::floor(height * heightScale);
2189 }
2190 else
2191 {
2192 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2193 }
2194
David Monahanf057e6f2020-06-22 09:55:23 +01002195 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2196 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002197
Kevin May42477c12020-03-26 13:34:14 +00002198 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002199 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2200 {
2201 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2202 IsResizeSupported,
2203 data.m_Backends,
2204 isSupported,
2205 inputInfo,
2206 outputInfo,
2207 descriptor);
2208 };
2209
2210 if(IsDynamicTensor(outputInfo))
2211 {
2212 isSupported = AreDynamicTensorsSupported();
2213 }
2214 else
2215 {
2216 validateFunc(outputInfo, isSupported);
2217 }
Kevin May42477c12020-03-26 13:34:14 +00002218
2219 if (!isSupported)
2220 {
2221 return false;
2222 }
2223
2224 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Kevin May42477c12020-03-26 13:34:14 +00002225 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +00002226 input.Connect(layer->GetInputSlot(0));
2227
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002228 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002229}
2230
2231template<typename HalPolicy,
2232 typename HalOperation = typename HalPolicy::Operation,
2233 typename HalModel = typename HalPolicy::Model>
2234bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2235{
2236 using HalOperand = typename HalPolicy::Operand;
2237 using HalOperandType = typename HalPolicy::OperandType;
2238
2239 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2240
2241 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2242 if (!input.IsValid() )
2243 {
2244 return Fail("%s: Operation has invalid inputs", __func__);
2245 }
2246
2247 const TensorInfo& inputInfo = input.GetTensorInfo();
2248 unsigned int rank = inputInfo.GetNumDimensions();
2249 if (rank != 4)
2250 {
2251 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2252 }
2253
2254 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2255 if (!output)
2256 {
2257 return Fail("%s: Could not read output 0", __func__);
2258 }
2259
2260 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002261
2262 SpaceToDepthDescriptor desc;
2263
2264 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2265
2266 if (desc.m_BlockSize <= 1)
2267 {
2268 return Fail("%s: Block size must be at least 1 in all dimensions");
2269 }
2270
2271 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2272
2273 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002274 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2275 {
2276 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2277 IsSpaceToDepthSupported,
2278 data.m_Backends,
2279 isSupported,
2280 inputInfo,
2281 outputInfo,
2282 desc);
2283 };
2284
2285 if(IsDynamicTensor(outputInfo))
2286 {
2287 isSupported = AreDynamicTensorsSupported();
2288 }
2289 else
2290 {
2291 validateFunc(outputInfo, isSupported);
2292 }
2293
Kevin May42477c12020-03-26 13:34:14 +00002294 if (!isSupported)
2295 {
2296 return false;
2297 }
2298
2299 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
2300 assert(layer != nullptr);
2301 input.Connect(layer->GetInputSlot(0));
2302
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002303 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002304}
2305
2306template<typename HalPolicy,
2307 typename HalOperation = typename HalPolicy::Operation,
2308 typename HalModel = typename HalPolicy::Model>
2309bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2310{
2311 using HalOperand = typename HalPolicy::Operand;
2312 using HalOperandType = typename HalPolicy::OperandType;
2313
2314 ALOGV("HalPolicy::ConvertSoftmax()");
2315
2316 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2317 if (!input.IsValid())
2318 {
2319 return Fail("%s: Operation has invalid inputs", __func__);
2320 }
2321
2322 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2323 if (!outputOperand)
2324 {
2325 return Fail("%s: Operation has no outputs", __func__);
2326 }
2327
2328 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002329
2330 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002331 HalOperandType outputType = outputOperand->type;
2332
2333 // Read beta value
2334 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002335 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002336 Half value;
2337
2338 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2339 {
2340 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2341 }
2342
2343 desc.m_Beta = static_cast<float>(value);
2344 }
2345 else
2346 {
2347 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2348 {
2349 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2350 }
Kevin May42477c12020-03-26 13:34:14 +00002351 }
2352
2353 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002354 2,
2355 HalOperandType::INT32,
2356 desc.m_Axis,
2357 model,
2358 data))
Kevin May42477c12020-03-26 13:34:14 +00002359 {
2360 return Fail("%s: Operation has invalid inputs", __func__);
2361 }
2362
Kevin May42477c12020-03-26 13:34:14 +00002363 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002364 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2365 {
2366 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2367 IsSoftmaxSupported,
2368 data.m_Backends,
2369 isSupported,
2370 input.GetTensorInfo(),
2371 outputInfo,
2372 desc);
2373 };
2374
2375 if(IsDynamicTensor(outputInfo))
2376 {
2377 isSupported = AreDynamicTensorsSupported();
2378 }
2379 else
2380 {
2381 validateFunc(outputInfo, isSupported);
2382 }
2383
Kevin May42477c12020-03-26 13:34:14 +00002384 if (!isSupported)
2385 {
2386 return false;
2387 }
2388
2389 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
2390 assert(layer != nullptr);
2391 input.Connect(layer->GetInputSlot(0));
2392
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002393 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002394}
2395
2396template<typename HalPolicy,
2397 typename HalOperation = typename HalPolicy::Operation,
2398 typename HalModel = typename HalPolicy::Model>
2399bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2400{
2401 using HalOperand = typename HalPolicy::Operand;
2402 using HalOperandType = typename HalPolicy::OperandType;
2403
2404 ALOGV("HalPolicy::ConvertLstm()");
2405
2406 // Inputs:
2407 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2408 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2409 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2410 if (!input.IsValid())
2411 {
2412 return Fail("%s: Could not read input 0: input", __func__);
2413 }
2414 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2415 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2416 if (!outputStateIn.IsValid())
2417 {
2418 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2419 }
2420 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2421 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2422 if (!cellStateIn.IsValid())
2423 {
2424 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2425 }
2426
2427 // Get the mandatory input tensors:
2428 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2429 // [num_units, input_size].
2430 const ConstTensorPin inputToForgetWeightsPin =
2431 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2432 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2433 // [num_units, input_size].
2434 const ConstTensorPin inputToCellWeightsPin =
2435 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2436 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2437 // [num_units, input_size].
2438 const ConstTensorPin inputToOutputWeightsPin =
2439 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2440 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2441 // [num_units, output_size].
2442 const ConstTensorPin recurrentToForgetWeightsPin =
2443 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2444 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2445 // [num_units, output_size].
2446 const ConstTensorPin recurrentToCellWeightsPin =
2447 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2448 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2449 // [num_units, output_size].
2450 const ConstTensorPin recurrentToOutputWeightsPin =
2451 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2452 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2453 const ConstTensorPin forgetGateBiasPin =
2454 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2455 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2456 const ConstTensorPin cellBiasPin =
2457 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2458 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2459 const ConstTensorPin outputGateBiasPin =
2460 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2461
2462 if (!inputToForgetWeightsPin.IsValid() ||
2463 !inputToCellWeightsPin.IsValid() ||
2464 !inputToOutputWeightsPin.IsValid() ||
2465 !recurrentToForgetWeightsPin.IsValid() ||
2466 !recurrentToCellWeightsPin.IsValid() ||
2467 !recurrentToOutputWeightsPin.IsValid() ||
2468 !forgetGateBiasPin.IsValid() ||
2469 !cellBiasPin.IsValid() ||
2470 !outputGateBiasPin.IsValid())
2471 {
2472 return Fail("%s: Operation has invalid tensor inputs", __func__);
2473 }
2474
2475 // Get the optional input tensors:
2476 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2477 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2478 const ConstTensorPin inputToInputWeightsPin =
2479 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2480 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2481 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2482 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2483 const ConstTensorPin recurrentToInputWeightsPin =
2484 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2485 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2486 const ConstTensorPin cellToInputWeightsPin =
2487 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2488 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2489 const ConstTensorPin cellToForgetWeightsPin =
2490 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2491 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2492 const ConstTensorPin cellToOutputWeightsPin =
2493 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2494 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2495 const ConstTensorPin inputGateBiasPin =
2496 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2497 12,
2498 model,
2499 data,
2500 g_DontPermute,
2501 nullptr,
2502 true);
2503
2504 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2505 // [output_size, num_units].
2506 const ConstTensorPin projectionWeightsPin =
2507 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2508 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2509 const ConstTensorPin projectionBiasPin =
2510 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2511 17,
2512 model,
2513 data,
2514 g_DontPermute,
2515 nullptr,
2516 true);
2517
2518 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2519 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2520 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2521 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2522 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2523 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2524 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2525 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2526 {
2527 return Fail("%s: Operation has invalid tensor inputs", __func__);
2528 }
2529
2530 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2531 // 20: The activation function: A value indicating the activation function:
2532 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2533 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2534 // If set to 0.0 then clipping is disabled.
2535 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2536 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2537 ActivationFn activation;
2538 float cellClip;
2539 float projClip;
2540 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2541 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2542 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2543 {
2544 return Fail("%s: Operation has invalid scalar inputs", __func__);
2545 }
2546
2547 // Get the normalization tensors
2548 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2549 // Used to rescale normalized inputs to activation at input gate.
2550 const ConstTensorPin inputLayerNormWeightsPin
2551 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2552
2553 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2554 // Used to rescale normalized inputs to activation at forget gate.
2555 const ConstTensorPin forgetLayerNormWeightsPin =
2556 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2557 24,
2558 model,
2559 data,
2560 g_DontPermute,
2561 nullptr,
2562 true);
2563
2564 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2565 // Used to rescale normalized inputs to activation at cell gate.
2566 const ConstTensorPin cellLayerNormWeightsPin =
2567 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2568 25,
2569 model,
2570 data,
2571 g_DontPermute,
2572 nullptr,
2573 true);
2574
2575 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2576 // Used to rescale normalized inputs to activation at output gate.
2577 const ConstTensorPin outputLayerNormWeightsPin =
2578 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2579 26,
2580 model,
2581 data,
2582 g_DontPermute,
2583 nullptr,
2584 true);
2585
2586 // Outputs:
2587 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2588 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2589 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2590 if (!scratchBuffer)
2591 {
2592 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2593 }
2594 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2595 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2596 if (!outputStateOut)
2597 {
2598 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2599 }
2600 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2601 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2602 if (!cellStateOut)
2603 {
2604 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2605 }
2606 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2607 // effectively the same as the current “output state (out)” value.
2608 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2609 if (!output)
2610 {
2611 return Fail("%s: Could not read output 3: output", __func__);
2612 }
2613
2614 // set the params structure for the AddLstmLayer call
2615 LstmInputParams params;
2616 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2617 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2618 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2619 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2620 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2621 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2622 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2623 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2624 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2625 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2626 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2627 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2628 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2629 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2630 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2631 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2632 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2633 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2634 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2635 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2636 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2637
2638 // set the layer descriptor
2639 LstmDescriptor desc;
2640 desc.m_ActivationFunc = activation;
2641 desc.m_ClippingThresCell = cellClip;
2642 desc.m_ClippingThresProj = projClip;
2643 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2644 params.m_RecurrentToInputWeights == nullptr ||
2645 params.m_InputGateBias == nullptr);
2646 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2647 params.m_CellToOutputWeights != nullptr);
2648 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2649 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2650 params.m_ForgetLayerNormWeights != nullptr ||
2651 params.m_CellLayerNormWeights != nullptr ||
2652 params.m_OutputLayerNormWeights != nullptr);
2653
2654 // validate the optional input groups
2655 if (desc.m_CifgEnabled &&
2656 (params.m_InputToInputWeights != nullptr ||
2657 params.m_RecurrentToInputWeights != nullptr ||
2658 params.m_InputGateBias != nullptr))
2659 {
2660 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2661 " and input gate bias must be provided", __func__);
2662 }
2663
2664 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2665 {
2666 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2667 }
2668
2669 if (desc.m_PeepholeEnabled &&
2670 (params.m_CellToForgetWeights == nullptr ||
2671 params.m_CellToOutputWeights == nullptr ||
2672 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2673 {
2674 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2675 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2676 }
2677
2678 if (desc.m_LayerNormEnabled &&
2679 (params.m_ForgetLayerNormWeights == nullptr ||
2680 params.m_CellLayerNormWeights == nullptr ||
2681 params.m_OutputLayerNormWeights == nullptr ||
2682 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2683 {
2684 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2685 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2686 }
2687
2688 // Check if the layer is supported
2689 // Inputs
2690 const TensorInfo& inputInfo = input.GetTensorInfo();
2691 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2692 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2693
2694 // Outputs
2695 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2696 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2697 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2698 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2699
Kevin May42477c12020-03-26 13:34:14 +00002700 // Basic parameters
2701 LstmInputParamsInfo paramsInfo;
2702 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2703 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2704 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2705 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2706 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2707 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2708 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2709 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2710 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2711
2712 // Optional parameters
2713 if (!desc.m_CifgEnabled)
2714 {
2715 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2716 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2717 if (params.m_CellToInputWeights != nullptr)
2718 {
2719 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2720 }
2721 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2722 }
2723
2724 if (desc.m_ProjectionEnabled)
2725 {
2726 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2727 if (params.m_ProjectionBias != nullptr)
2728 {
2729 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2730 }
2731 }
2732
2733 if (desc.m_PeepholeEnabled)
2734 {
2735 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2736 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2737 }
2738
2739 if (desc.m_LayerNormEnabled)
2740 {
2741 if(!desc.m_CifgEnabled)
2742 {
2743 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2744 }
2745 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2746 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2747 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2748 }
2749
2750 bool isSupported = false;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002751 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2752 {
2753 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2754 IsLstmSupported,
2755 data.m_Backends,
2756 isSupported,
2757 inputInfo,
2758 outputStateInInfo,
2759 cellStateInInfo,
2760 scratchBufferInfo,
2761 outputStateOutInfo,
2762 cellStateOutInfo,
2763 outputInfo,
2764 desc,
2765 paramsInfo);
2766 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002767
Sadik Armagandbda4b72020-09-03 11:33:07 +01002768 bool isDynamic = false;
2769 if (!IsDynamicTensor(outputStateOutInfo) &&
2770 !IsDynamicTensor(scratchBufferInfo) &&
2771 !IsDynamicTensor(cellStateOutInfo) &&
2772 !IsDynamicTensor(outputInfo))
2773 {
2774 validateFunc(outputInfo, isSupported);
2775 }
2776 else
2777 {
2778 isDynamic = true;
2779 isSupported = AreDynamicTensorsSupported();
2780 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002781
Kevin May42477c12020-03-26 13:34:14 +00002782 if (!isSupported)
2783 {
2784 return false;
2785 }
2786
2787 // Add the layer
2788 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2789
2790 input.Connect(layer->GetInputSlot(0));
2791 outputStateIn.Connect(layer->GetInputSlot(1));
2792 cellStateIn.Connect(layer->GetInputSlot(2));
2793
Sadik Armagandbda4b72020-09-03 11:33:07 +01002794 if (!isDynamic)
2795 {
2796 return (
2797 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2798 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2799 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2800 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2801 }
2802 else
2803 {
2804 return (
2805 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2806 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2807 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2808 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002809 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002810 }
2811
Kevin May42477c12020-03-26 13:34:14 +00002812}
2813
2814template<typename HalPolicy,
2815 typename HalOperation = typename HalPolicy::Operation,
2816 typename HalModel = typename HalPolicy::Model>
2817bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2818{
2819 using HalOperand = typename HalPolicy::Operand;
2820 using HalOperandType = typename HalPolicy::OperandType;
2821
2822 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2823
2824 if (!input.IsValid())
2825 {
2826 return Fail("%s: Operation has invalid inputs", __func__);
2827 }
2828
2829 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2830
2831 if (!output)
2832 {
2833 return Fail("%s: Could not read output 0", __func__);
2834 }
2835
2836 const TensorInfo& inputInfo = input.GetTensorInfo();
2837 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002838
2839 // ArmNN does not currently support non-fixed weights or bias
2840 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2841 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2842
2843 if (weightsOperand == nullptr)
2844 {
2845 return Fail("%s: Operand is invalid", __func__);
2846 }
2847 TransposeConvolution2dDescriptor desc;
2848 desc.m_DataLayout = DataLayout::NHWC;
2849
2850 // Determine whether padding is implicit or explicit
2851 bool implicitPadding = operation.inputs.size() == 9;
2852
2853 if (implicitPadding )
2854 {
2855 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2856 }
2857 else
2858 {
2859 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2860 }
2861
2862 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2863 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2864 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2865
2866 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2867
2868 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2869 // We have to permute it to OIHW if the data layout is NCHW.
2870 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2871 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2872 model, data, OHWIToOIHW) :
2873 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2874
2875 // Bias is a 1D tensor
2876 const ConstTensorPin biasPin =
2877 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2878
2879 if (!weightsPin.IsValid())
2880 {
2881 return Fail("%s: Operation has invalid weights", __func__);
2882 }
2883
2884 if (!biasPin.IsValid())
2885 {
2886 return Fail("%s: Operation has invalid biases", __func__);
2887 }
2888
2889 ConstTensor weights = weightsPin.GetConstTensor();
2890 ConstTensor bias = biasPin.GetConstTensor();
2891 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2892
2893 ActivationFn activation;
2894
2895 if (implicitPadding)
2896 {
2897 int32_t strideX{0};
2898 int32_t strideY{0};
2899 int32_t padLeft{0};
2900 int32_t padRight{0};
2901 int32_t padTop{0};
2902 int32_t padBottom{0};
2903
2904 android::nn::PaddingScheme paddingScheme;
2905 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2906 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2907 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2908 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2909 {
2910 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2911 }
2912
2913 const uint32_t kernelX = weights.GetShape()[widthIndex];
2914 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00002915
Colm Donelan8f3d33e2020-07-06 15:41:43 +01002916 // If output shape has been specified as a parameter then extract it and make it available.
2917 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
2918 std::vector<int32_t> outputShape;
2919 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
2920 {
2921 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
2922 for (int dimension : outputShape)
2923 {
2924 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
2925 }
2926 desc.m_OutputShapeEnabled = true;
2927 }
2928
Finn Williams8fe50c62020-10-09 15:52:57 +01002929 uint32_t outputX;
2930 uint32_t outputY;
2931
2932 if (IsDynamicTensor(outputInfo))
2933 {
2934 if (outputShape.size() == 0)
2935 {
2936 return Fail("%s: Padding sizes cannot be inferred", __func__);
2937 }
2938
2939 outputX = outputShape[widthIndex];
2940 outputY = outputShape[heightIndex];
2941 }
2942 else
2943 {
2944 outputX = outputInfo.GetShape()[widthIndex];
2945 outputY = outputInfo.GetShape()[heightIndex];
2946 }
2947
2948 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2949 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
2950
2951 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2952 // but Arm NN only supports values >= 0
2953 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2954 {
2955 return Fail("%s: Negative padding values are not supported", __func__);
2956 }
2957
Matthew Sloyan9b088d92020-09-14 15:12:55 +01002958 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
2959 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
2960 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
2961 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
2962 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
2963 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00002964 }
2965 else if (operation.inputs.size() == 11)
2966 {
2967 // explicit padding
2968 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2969 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2970 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2971 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2972 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2973 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
2974 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
2975 {
2976 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2977 }
2978 }
2979 else
2980 {
2981 return Fail("%s: Unsupported number of operation inputs", __func__);
2982 }
2983
2984 desc.m_BiasEnabled = true;
2985 Optional<TensorInfo> biases(bias.GetInfo());
2986
2987 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002988 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2989 {
2990 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2991 IsTransposeConvolution2dSupported,
2992 data.m_Backends,
2993 isSupported,
2994 inputInfo,
2995 outputInfo,
2996 desc,
2997 weights.GetInfo(),
2998 biases);
2999 };
3000
3001 if(IsDynamicTensor(outputInfo))
3002 {
3003 isSupported = AreDynamicTensorsSupported();
3004 }
3005 else
3006 {
3007 validateFunc(outputInfo, isSupported);
3008 }
Kevin May42477c12020-03-26 13:34:14 +00003009 if (!isSupported)
3010 {
3011 return false;
3012 }
3013
3014 IConnectableLayer* startLayer =
3015 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
3016 if (!startLayer)
3017 {
3018 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
3019 }
3020
Kevin May42477c12020-03-26 13:34:14 +00003021 input.Connect(startLayer->GetInputSlot(0));
3022
Kevin Mayfcf2a152020-09-08 16:06:32 +01003023 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
3024 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00003025}
3026
3027} // armnn_driver namespace