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