blob: 779d88f4821b3803818053538c64f8cb8a2e528f [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
656 if (targetShape != outputInfo.GetShape())
657 {
658 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
659 }
660
661 ReshapeDescriptor reshapeDescriptor;
662 reshapeDescriptor.m_TargetShape = targetShape;
663
664 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100665 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
666 {
667 FORWARD_LAYER_SUPPORT_FUNC(__func__,
668 IsReshapeSupported,
669 data.m_Backends,
670 isSupported,
671 input.GetTensorInfo(),
672 outputInfo,
673 reshapeDescriptor);
674 };
675
676 if(!IsDynamicTensor(outputInfo))
677 {
678 validateFunc(outputInfo, isSupported);
679 }
680 else
681 {
682 isSupported = AreDynamicTensorsSupported();
683 }
Kevin May42477c12020-03-26 13:34:14 +0000684
685 if (!isSupported)
686 {
687 return false;
688 }
689
690 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
691 assert(layer != nullptr);
692 input.Connect(layer->GetInputSlot(0));
693
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100694 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000695}
696
697template<typename HalPolicy,
Teresa Charlinf931af92020-04-10 16:46:53 +0100698 typename HalOperation = typename HalPolicy::Operation,
699 typename HalModel = typename HalPolicy::Model>
700bool ConvertGather(const HalOperation& operation, const HalModel& model, ConversionData& data)
701{
702 using HalOperand = typename HalPolicy::Operand;
703 using HalOperandType = typename HalPolicy::OperandType;
704
705 ALOGV("HalPolicy::ConvertGather()");
706
707 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
708 if (!input.IsValid())
709 {
710 return Fail("%s: Operation has invalid input", __func__);
711 }
712 auto inputDimensions = input.GetTensorInfo().GetNumDimensions();
713
714 LayerInputHandle indices = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data);
715 if (!indices.IsValid())
716 {
717 return Fail("%s: Operation has invalid indices", __func__);
718 }
719 auto indicesDimensions = indices.GetTensorInfo().GetNumDimensions();
720
721 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
722 if (!output)
723 {
724 return Fail("%s: Operation has invalid output", __func__);
725 }
726 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
727 auto outputDimensions = outputInfo.GetNumDimensions();
Teresa Charlinf931af92020-04-10 16:46:53 +0100728 if (outputDimensions != inputDimensions + indicesDimensions - 1)
729 {
730 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 +0100731 __func__, outputDimensions, inputDimensions, indicesDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100732 }
733
734 int32_t axis;
735 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
736 {
737 return Fail("%s: Operation has invalid or unsupported axis operand", __func__);
738 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100739 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
Teresa Charlinf931af92020-04-10 16:46:53 +0100740 {
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100741 return Fail("%s: Operation has invalid axis: %d. It is out of bounds [-%d, %d))", __func__, axis,
742 inputDimensions, inputDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100743 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100744
745 GatherDescriptor desc;
746 desc.m_Axis = axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100747
748 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100749 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
750 {
751 FORWARD_LAYER_SUPPORT_FUNC(__func__,
752 IsGatherSupported,
753 data.m_Backends,
754 isSupported,
755 input.GetTensorInfo(),
756 indices.GetTensorInfo(),
757 outputInfo,
758 desc);
759 };
760
761 if(!IsDynamicTensor(outputInfo))
762 {
763 validateFunc(outputInfo, isSupported);
764 }
765 else
766 {
767 isSupported = AreDynamicTensorsSupported();
768 }
769
Teresa Charlinf931af92020-04-10 16:46:53 +0100770 if (!isSupported)
771 {
772 return false;
773 }
774
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100775 IConnectableLayer* layer = data.m_Network->AddGatherLayer(desc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100776 assert(layer != nullptr);
777 input.Connect(layer->GetInputSlot(0));
778 indices.Connect(layer->GetInputSlot(1));
779
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100780 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100781}
782
783template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000784 typename HalOperation = typename HalPolicy::Operation,
785 typename HalModel = typename HalPolicy::Model>
786bool ConvertGroupedConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
787{
788 using HalOperand = typename HalPolicy::Operand;
789 using HalOperandType = typename HalPolicy::OperandType;
790
791 ALOGV("HalPolicy::ConvertGroupedConv2d()");
792
793 //
794 // Parse data
795 //
796 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
797 if (!input.IsValid())
798 {
799 return Fail("%s: Operation has invalid inputs", __func__);
800 }
801 const TensorInfo& inputInfo = input.GetTensorInfo();
802
803 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
804 if (!output)
805 {
806 return Fail("%s: Could not read output 0", __func__);
807 }
Finn Williamsb0331172020-10-08 14:33:13 +0100808 TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000809
810 // Look ahead to determine data layout
811 DataLayout dataLayout = DataLayout::NHWC;
812 if (operation.inputs.size() == 12)
813 {
814 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
815 }
816 else
817 {
818 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
819 }
820
821 // NOTE:
822 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
823 // but Arm NN expects the filter's height and width indices to match the input's height and
824 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
825 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
826 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
827 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
828 model, data, ohwiToOihw) :
829 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
830 const ConstTensorPin biasesPin =
831 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
832 if (!weightsPin.IsValid() || !biasesPin.IsValid())
833 {
834 return Fail("%s: Operation has invalid inputs", __func__);
835 }
836
837 ConstTensor weights = weightsPin.GetConstTensor();
838 ConstTensor biases = biasesPin.GetConstTensor();
839 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
840
841 const TensorShape& inputShape = inputInfo.GetShape();
842 const TensorShape& outputShape = outputInfo.GetShape();
843 const TensorShape& weightsShape = weights.GetShape();
844 const TensorShape& biasesShape = biases.GetShape();
845
846 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
847 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
848 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
849 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
850
851 Convolution2dDescriptor desc;
852 desc.m_DataLayout = dataLayout;
853 desc.m_BiasEnabled = true;
854
855 int numGroups;
856 ActivationFn activation;
857
858 if (operation.inputs.size() == 12)
859 {
860 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
861 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
862 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
863 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
864 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
865 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
866 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
867 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
868 {
869 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
870 }
871
872 }
873 else if (operation.inputs.size() == 9)
874 {
875 android::nn::PaddingScheme paddingScheme;
876 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
877 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
878 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
879 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
880 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
881 {
882 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
883 }
884
885 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
886 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
887
888 const uint32_t kernelX = weightsShape[widthIndex];
889 const uint32_t kernelY = weightsShape[heightIndex];
890
891 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
892 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
893 }
894 else
895 {
896 return Fail("%s: Unsupported number of operation inputs", __func__);
897 }
898
Finn Williamsb0331172020-10-08 14:33:13 +0100899 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
900 const unsigned int outputChannels = weightsShape[0];
Kevin May42477c12020-03-26 13:34:14 +0000901
902 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
903 const unsigned int channelMultiplier = outputChannels / numGroups;
904
905 //
906 // Validate all relevant inputs
907 //
908 if (numGroups <= 0)
909 {
910 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
911 }
912
913 if (outputChannels % numGroups != 0u)
914 {
915 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
916 }
917
918 //
919 // Set up Splitter layer
920 //
921 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
922 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
923
924 TensorInfo splitterOutputInfo(4,
925 splitterDimSizes,
926 inputInfo.GetDataType(),
927 inputInfo.GetQuantizationScale(),
928 inputInfo.GetQuantizationOffset());
929
930 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
931
932 ViewsDescriptor splitterDesc(numGroups);
933 for (unsigned int group = 0u; group < numGroups; ++group)
934 {
935 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
936 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
937 {
938 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
939 }
940 }
941
942 bool isSupported = false;
943 FORWARD_LAYER_SUPPORT_FUNC(__func__,
944 IsSplitterSupported,
945 data.m_Backends,
946 isSupported,
947 inputInfo,
948 splitterOutputInfos,
949 splitterDesc);
950 if (!isSupported)
951 {
952 return false;
953 }
954
955 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
956 if (!splitterLayer)
957 {
958 return Fail("%s: Failed to add SplitterLayer", __func__);
959 }
960
961 input.Connect(splitterLayer->GetInputSlot(0));
962 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
963 {
964 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
965 }
966
967 //
968 // Set up Convolution2d layers for each group
969 //
970
971 // Set up group tensor shapes
972 TensorShape groupInputShape(inputShape);
973 groupInputShape[channelsIndex] = channelsPerGroup;
974
Kevin May42477c12020-03-26 13:34:14 +0000975 TensorShape groupWeightsShape(weightsShape);
976 groupWeightsShape[0] /= channelMultiplier * numGroups;
977
978 TensorShape groupBiasesShape({ 1 });
979
980 // Set up group tensor infos
981 TensorInfo groupInputInfo(inputInfo);
982 groupInputInfo.SetShape(groupInputShape);
983
984 const TensorInfo& weightsInfo = weights.GetInfo();
985 TensorInfo groupWeightsInfo(weightsInfo);
986 groupWeightsInfo.SetShape(groupWeightsShape);
987
988 const TensorInfo& biasesInfo = biases.GetInfo();
989 TensorInfo groupBiasesInfo(biasesInfo);
990 groupBiasesInfo.SetShape(groupBiasesShape);
991
992 TensorInfo groupOutputInfo(outputInfo);
Finn Williamsb0331172020-10-08 14:33:13 +0100993
994 TensorShape groupOutputShape(outputShape);
995 const bool isDynamic = IsDynamicTensor(outputInfo);
996 if (!isDynamic)
997 {
998 groupOutputShape[channelsIndex] = 1;
999 }
Kevin May42477c12020-03-26 13:34:14 +00001000 groupOutputInfo.SetShape(groupOutputShape);
1001
1002 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1003 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1004
1005 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1006 for (unsigned int group = 0u; group < numGroups; ++group)
1007 {
1008 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1009 {
1010 auto index = group * channelMultiplier + m;
1011
1012 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1013 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1014
1015 if (weightsInfo.HasPerAxisQuantization())
1016 {
1017 // Extract per-axis quantization scales for group weights
1018 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1019 groupWeightsInfo.SetQuantizationScales(
1020 std::vector<float>(weightsQuantScales.begin() + index,
1021 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1022
1023 // Extract per-axis quantization scales for group biases
1024 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1025 groupBiasesInfo.SetQuantizationScales(
1026 std::vector<float>(biasesQuantScales.begin() + index,
1027 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1028 }
1029
1030 // Extract weights and biases data for current group convolution
1031 ConstTensor groupWeights(groupWeightsInfo,
1032 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1033 weightsDataOffset));
1034 ConstTensor groupBiases(groupBiasesInfo,
1035 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1036 biasesDataOffset));
1037
1038 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001039 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1040 {
1041 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1042 IsConvolution2dSupported,
1043 data.m_Backends,
1044 isSupported,
1045 groupInputInfo,
1046 outputInfo,
1047 desc,
1048 groupWeightsInfo,
1049 Optional<TensorInfo>(groupBiasesInfo));
1050 };
1051
1052 if(!isDynamic)
1053 {
1054 validateFunc(groupOutputInfo, isSupported);
1055 }
1056 else
1057 {
1058 isSupported = AreDynamicTensorsSupported();
1059 }
1060
Kevin May42477c12020-03-26 13:34:14 +00001061 if (!isSupported)
1062 {
1063 return false;
1064 }
1065
1066 IConnectableLayer* convLayer =
1067 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1068 if (!convLayer)
1069 {
1070 return Fail("%s: AddConvolution2dLayer failed", __func__);
1071 }
1072
1073 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1074 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1075
Finn Williamsb0331172020-10-08 14:33:13 +01001076 if(isDynamic)
1077 {
1078 convLayer->GetOutputSlot(0).IsTensorInfoSet();
1079
1080 validateFunc(convLayer->GetOutputSlot(0).GetTensorInfo(), isSupported);
1081
1082 outputInfo = convLayer->GetOutputSlot(0).GetTensorInfo();
1083
1084 if (!isSupported)
1085 {
1086 return false;
1087 }
1088 }
1089
Kevin May42477c12020-03-26 13:34:14 +00001090 convLayers[index] = convLayer;
1091 }
1092 }
1093
1094 //
1095 // Set up Concat layer
1096 //
Finn Williamsb0331172020-10-08 14:33:13 +01001097 ConcatDescriptor concatDescriptor;
1098 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1099 concatDescriptor = ConcatDescriptor(weightsShape[0]);
Kevin May42477c12020-03-26 13:34:14 +00001100 for (unsigned int group = 0u; group < numGroups; ++group)
1101 {
1102 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1103 {
1104 auto index = group * channelMultiplier + m;
1105 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1106 concatDescriptor.SetConcatAxis(channelsIndex);
1107 }
1108 }
1109
1110 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001111 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1112 IsConcatSupported,
1113 data.m_Backends,
1114 isSupported,
1115 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1116 outputInfo,
1117 concatDescriptor);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001118
Kevin May42477c12020-03-26 13:34:14 +00001119 if (!isSupported)
1120 {
1121 return false;
1122 }
1123
1124 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1125 if (!concatLayer)
1126 {
1127 return Fail("%s: AddConcatLayer failed", __func__);
1128 }
1129
1130 for (unsigned int group = 0u; group < numGroups; ++group)
1131 {
1132 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1133 {
1134 auto index = group * channelMultiplier + m;
1135 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1136 }
1137 }
1138 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1139
Kevin Mayfcf2a152020-09-08 16:06:32 +01001140 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
Finn Williamsb0331172020-10-08 14:33:13 +01001141 data, nullptr, nullptr, activation);
Kevin May42477c12020-03-26 13:34:14 +00001142}
1143
1144template<typename HalPolicy,
1145 typename HalOperation = typename HalPolicy::Operation,
1146 typename HalModel = typename HalPolicy::Model>
1147bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1148{
1149 using HalOperand = typename HalPolicy::Operand;
1150 using HalOperandType = typename HalPolicy::OperandType;
1151
1152 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1153
1154 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1155 if (!input.IsValid())
1156 {
1157 return Fail("%s: Operation has an invalid input 0", __func__);
1158 }
1159
1160 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1161 if (!output)
1162 {
1163 return Fail("%s: Operation has an invalid output", __func__);
1164 }
1165
1166 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001167
1168 // Determine data type of input tensor
1169 HalOperandType inputType;
1170 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1171 {
1172 return Fail("%s: Operation has invalid inputs", __func__);
1173 }
1174
1175 InstanceNormalizationDescriptor desc;
1176
1177 // Read gamma, beta & epsilon
1178 if (inputType == HalOperandType::TENSOR_FLOAT16)
1179 {
1180 Half fp16Gamma;
1181 Half fp16Beta;
1182 Half fp16Epsilon;
1183
1184 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1185 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1186 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1187 {
1188 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1189 }
1190
1191 desc.m_Gamma = static_cast<float>(fp16Gamma);
1192 desc.m_Beta = static_cast<float>(fp16Beta);
1193 desc.m_Eps = static_cast<float>(fp16Epsilon);
1194 }
1195 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1196 {
1197 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1198 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1199 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1200 {
1201 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1202 }
1203 }
1204 else
1205 {
1206 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1207 }
1208
1209 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1210
1211 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001212 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1213 {
1214 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1215 IsInstanceNormalizationSupported,
1216 data.m_Backends,
1217 isSupported,
1218 input.GetTensorInfo(),
1219 outputInfo,
1220 desc);
1221 };
1222
1223 if(IsDynamicTensor(outputInfo))
1224 {
1225 isSupported = AreDynamicTensorsSupported();
1226 }
1227 else
1228 {
1229 validateFunc(outputInfo, isSupported);
1230 }
1231
Kevin May42477c12020-03-26 13:34:14 +00001232 if (!isSupported)
1233 {
1234 return false;
1235 }
1236
1237 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1238 input.Connect(layer->GetInputSlot(0));
1239
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001240 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001241}
1242
1243template<typename HalPolicy,
1244 typename HalOperation = typename HalPolicy::Operation,
1245 typename HalModel = typename HalPolicy::Model>
1246bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1247{
1248 using HalOperand = typename HalPolicy::Operand;
1249 using HalOperandType = typename HalPolicy::OperandType;
1250
1251 ALOGV("HalPolicy::ConvertLogSoftmax()");
1252
1253 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1254 if (!input.IsValid())
1255 {
1256 return Fail("%s: Failed to read input 0", __func__);
1257 }
1258
1259 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1260 if (!output)
1261 {
1262 return Fail("%s: Failed to read output", __func__);
1263 }
1264
1265 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001266
1267 // Determine data type of input tensor
1268 HalOperandType inputType;
1269 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1270 {
1271 return Fail("%s: Operation has invalid inputs", __func__);
1272 }
1273
1274 LogSoftmaxDescriptor descriptor;
1275
1276 // Read beta
1277 if (inputType == HalOperandType::TENSOR_FLOAT16)
1278 {
1279 Half fp16Beta;
1280 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1281 {
1282 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1283 }
1284
1285 descriptor.m_Beta = static_cast<float>(fp16Beta);
1286 }
1287 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1288 {
1289 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1290 {
1291 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1292 }
1293 }
1294 else
1295 {
1296 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1297 }
1298
1299 // Read axis
1300 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1301 {
1302 return Fail("%s: Failed to read input 2", __func__);
1303 }
1304
1305 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001306 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1307 {
1308 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1309 IsLogSoftmaxSupported,
1310 data.m_Backends,
1311 isSupported,
1312 input.GetTensorInfo(),
1313 outputInfo,
1314 descriptor);
1315 };
1316
1317 if(IsDynamicTensor(outputInfo))
1318 {
1319 isSupported = AreDynamicTensorsSupported();
1320 }
1321 else
1322 {
1323 validateFunc(outputInfo, isSupported);
1324 }
1325
Kevin May42477c12020-03-26 13:34:14 +00001326 if (!isSupported)
1327 {
1328 return false;
1329 }
1330
1331 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
1332 if (!layer)
1333 {
1334 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1335 }
1336
1337 input.Connect(layer->GetInputSlot(0));
1338
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001339 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001340}
1341
1342template<typename HalPolicy,
1343 typename HalOperation = typename HalPolicy::Operation,
1344 typename HalModel = typename HalPolicy::Model>
1345bool ConvertMaximum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1346{
1347 using HalOperand = typename HalPolicy::Operand;
1348
1349 ALOGV("HalPolicy::ConvertMaximum()");
1350
1351 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1352 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1353
1354 if (!input0.IsValid() || !input1.IsValid())
1355 {
1356 return Fail("%s: Operation has invalid inputs", __func__);
1357 }
1358
1359 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1360 if (!outputOperand)
1361 {
1362 return Fail("%s: Could not read output", __func__);
1363 }
1364
1365 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001366
1367 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001368 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
1369 {
1370 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1371 IsMaximumSupported,
1372 data.m_Backends,
1373 isSupported,
1374 input0.GetTensorInfo(),
1375 input1.GetTensorInfo(),
1376 outInfo);
1377 };
1378
1379 if(IsDynamicTensor(outInfo))
1380 {
1381 isSupported = AreDynamicTensorsSupported();
1382 }
1383 else
1384 {
1385 validateFunc(outInfo, isSupported);
1386 }
Kevin May42477c12020-03-26 13:34:14 +00001387
1388 if (!isSupported)
1389 {
1390 return false;
1391 }
1392
1393 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
1394 assert(layer != nullptr);
1395 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1396 if (!isReshapeSupported)
1397 {
1398 return false;
1399 }
1400
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001401 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001402}
1403
1404template<typename HalPolicy,
1405 typename HalOperation = typename HalPolicy::Operation,
1406 typename HalModel = typename HalPolicy::Model>
1407bool ConvertMinimum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1408{
1409 using HalOperand = typename HalPolicy::Operand;
1410
1411 ALOGV("HalPolicy::ConvertMinimum()");
1412
1413 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1414 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1415
1416 if (!input0.IsValid() || !input1.IsValid())
1417 {
1418 return Fail("%s: Operation has invalid inputs", __func__);
1419 }
1420
1421 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1422 if (!output)
1423 {
1424 return Fail("%s: Could not read output 0", __func__);
1425 }
1426
1427 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001428
1429 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001430 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1431 {
1432 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1433 IsMinimumSupported,
1434 data.m_Backends,
1435 isSupported,
1436 input0.GetTensorInfo(),
1437 input1.GetTensorInfo(),
1438 outputInfo);
1439 };
1440
1441 if(IsDynamicTensor(outputInfo))
1442 {
1443 isSupported = AreDynamicTensorsSupported();
1444 }
1445 else
1446 {
1447 validateFunc(outputInfo, isSupported);
1448 }
Kevin May42477c12020-03-26 13:34:14 +00001449
1450 if (!isSupported)
1451 {
1452 return false;
1453 }
1454
1455 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
1456 assert(layer != nullptr);
1457 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1458 if (!isReshapeSupported)
1459 {
1460 return false;
1461 }
1462
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001463 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001464}
1465
1466template<typename HalPolicy,
1467 typename HalOperation = typename HalPolicy::Operation,
1468 typename HalModel = typename HalPolicy::Model>
1469bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1470{
1471 using HalOperand = typename HalPolicy::Operand;
1472 using HalOperandType = typename HalPolicy::OperandType;
1473
1474 ALOGV("HalPolicy::ConvertPadV2()");
1475
1476 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1477 if (!input.IsValid())
1478 {
1479 return Fail("%s: Could not read input 0", __func__);
1480 }
1481
1482 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1483 if (!output)
1484 {
1485 return Fail("%s: Could not read output", __func__);
1486 }
1487
1488 const TensorInfo& inputInfo = input.GetTensorInfo();
1489 unsigned int rank = inputInfo.GetNumDimensions();
1490
1491 PadDescriptor descriptor;
1492 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1493 {
1494 return Fail("%s: Could not convert paddings", __func__);
1495 }
1496
1497 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001498
1499 // Determine type of padding value
1500 HalOperandType operandType0;
1501 HalOperandType operandType2;
1502
1503 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1504 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1505 {
1506 return Fail("%s: Operation has invalid inputs", __func__);
1507 }
1508
1509 // Read value to use for padding
1510 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1511 {
1512 Half f16PadValue;
1513 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1514 {
1515 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1516 }
1517
1518 descriptor.m_PadValue = f16PadValue;
1519 }
1520 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1521 {
1522 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1523 {
1524 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1525 }
1526 }
1527 else if (operandType0 == HalOperandType::TENSOR_QUANT8_ASYMM && operandType2 == HalOperandType::INT32)
1528 {
1529 int32_t intPadValue = 0;
1530 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1531 {
1532 return Fail("%s: Could not read input 2 (INT32)", __func__);
1533 }
1534 descriptor.m_PadValue = intPadValue;
1535 }
1536 else
1537 {
1538 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1539 }
1540
1541 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001542 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1543 {
1544 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1545 IsPadSupported,
1546 data.m_Backends,
1547 isSupported,
1548 inputInfo,
1549 outputInfo,
1550 descriptor);
1551 };
1552
1553 if(IsDynamicTensor(outputInfo))
1554 {
1555 isSupported = AreDynamicTensorsSupported();
1556 }
1557 else
1558 {
1559 validateFunc(outputInfo, isSupported);
1560 }
1561
Kevin May42477c12020-03-26 13:34:14 +00001562 if (!isSupported)
1563 {
1564 return false;
1565 }
1566
1567 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
1568 assert(layer != nullptr);
1569 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001570
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001571 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001572}
1573
1574template<typename HalPolicy,
1575 typename HalOperation = typename HalPolicy::Operation,
1576 typename HalModel = typename HalPolicy::Model>
1577bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1578{
1579 using HalOperand = typename HalPolicy::Operand;
1580
1581 ALOGV("HalPolicy::ConvertPrelu()");
1582
1583 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1584 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1585
1586 if (!input.IsValid() || !alpha.IsValid())
1587 {
1588 return Fail("%s: Operation has invalid inputs", __func__);
1589 }
1590
1591 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1592
1593 if (!output)
1594 {
1595 return Fail("%s: Could not read output", __func__);
1596 }
1597
1598 const TensorInfo& inputInfo = input.GetTensorInfo();
1599 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1600 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1601
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001602 bool isSupported = false;
1603 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001604 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001605 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1606 IsPreluSupported,
1607 data.m_Backends,
1608 isSupported,
1609 inputInfo,
1610 alphaInfo,
1611 outputInfo);
1612 };
1613
1614 if(IsDynamicTensor(outputInfo))
1615 {
1616 isSupported = AreDynamicTensorsSupported();
1617 }
1618 else
1619 {
1620 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001621 }
1622
Kevin May42477c12020-03-26 13:34:14 +00001623 if (!isSupported)
1624 {
1625 return false;
1626 }
1627
1628 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
1629
1630 if (!layer)
1631 {
1632 return Fail("%s: AddPreluLayer failed", __func__);
1633 }
1634
1635 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1636 if (!isReshapeSupported)
1637 {
1638 return false;
1639 }
1640
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001641 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001642}
1643
1644template<typename HalPolicy,
1645 typename HalOperation = typename HalPolicy::Operation,
1646 typename HalModel = typename HalPolicy::Model>
1647bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1648{
1649 using HalOperand = typename HalPolicy::Operand;
1650
1651 ALOGV("HalPolicy::ConvertQuantize()");
1652
1653 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1654 if (!input.IsValid())
1655 {
1656 return Fail("%s: Operation has invalid input", __func__);
1657 }
1658
1659 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1660 if (!outputOperand)
1661 {
1662 return Fail("%s: Operation has invalid outputs", __func__);
1663 }
1664
1665 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001666
1667 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001668 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1669 {
1670 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1671 IsQuantizeSupported,
1672 data.m_Backends,
1673 isSupported,
1674 input.GetTensorInfo(),
1675 outputInfo);
1676 };
1677
1678 if(IsDynamicTensor(outputInfo))
1679 {
1680 isSupported = AreDynamicTensorsSupported();
1681 }
1682 else
1683 {
1684 validateFunc(outputInfo, isSupported);
1685 }
1686
Kevin May42477c12020-03-26 13:34:14 +00001687 if (!isSupported)
1688 {
1689 return false;
1690 }
1691
1692 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
1693 assert(layer != nullptr);
1694 input.Connect(layer->GetInputSlot(0));
1695
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001696 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001697}
1698
1699template<typename HalPolicy,
1700 typename HalOperation = typename HalPolicy::Operation,
1701 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001702bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001703{
1704 using HalOperand = typename HalPolicy::Operand;
1705
Sadik Armagan813f2302020-05-19 14:10:30 +01001706 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001707
1708 //Inputs:
1709 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1710 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1711 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1712 if (!input.IsValid())
1713 {
1714 return Fail("%s: Could not read input 0: input", __func__);
1715 }
1716
1717 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1718 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1719 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1720 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1721 if (!previousCellStateIn.IsValid())
1722 {
1723 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1724 }
1725
1726 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1727 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1728 // is quantized with a fixed quantization range of -1, 127/128.
1729 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1730 if (!previousOutputIn.IsValid())
1731 {
1732 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1733 }
1734
1735 // Get the input tensors:
1736 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1737 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1738 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1739 const ConstTensorPin inputToInputWeightsPin =
1740 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1741
1742 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1743 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1744 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1745 const ConstTensorPin inputToForgetWeightsPin =
1746 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1747
1748 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1749 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1750 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1751 const ConstTensorPin inputToCellWeightsPin =
1752 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1753
1754 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1755 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1756 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1757 const ConstTensorPin inputToOutputWeightsPin =
1758 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1759
1760 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1761 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1762 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1763 const ConstTensorPin recurrentToInputWeightsPin =
1764 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1765
1766 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1767 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1768 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1769 const ConstTensorPin recurrentToForgetWeightsPin =
1770 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1771
1772 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1773 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1774 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1775 const ConstTensorPin recurrentToCellWeightsPin =
1776 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1777
1778 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1779 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1780 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1781 const ConstTensorPin recurrentToOutputWeightsPin =
1782 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1783
1784 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1785 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1786 // of input and weights scales and zeroPoint equal to 0.
1787 const ConstTensorPin inputGateBiasPin =
1788 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1789
1790 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1791 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1792 // of input and weights scales and zeroPoint equal to 0.
1793 const ConstTensorPin forgetGateBiasPin =
1794 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1795
1796 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1797 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1798 // and weights scales and zeroPoint equal to 0.
1799 const ConstTensorPin cellBiasPin =
1800 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1801
1802 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1803 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1804 // of input and weights scales and zeroPoint equal to 0.
1805 const ConstTensorPin outputGateBiasPin =
1806 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1807
1808 if (!inputToInputWeightsPin.IsValid() ||
1809 !inputToForgetWeightsPin.IsValid() ||
1810 !inputToCellWeightsPin.IsValid() ||
1811 !inputToOutputWeightsPin.IsValid() ||
1812 !recurrentToInputWeightsPin.IsValid() ||
1813 !recurrentToForgetWeightsPin.IsValid() ||
1814 !recurrentToCellWeightsPin.IsValid() ||
1815 !recurrentToOutputWeightsPin.IsValid() ||
1816 !inputGateBiasPin.IsValid() ||
1817 !forgetGateBiasPin.IsValid() ||
1818 !cellBiasPin.IsValid() ||
1819 !outputGateBiasPin.IsValid())
1820 {
1821 return Fail("%s: Operation has invalid tensor inputs", __func__);
1822 }
1823
1824 // Outputs:
1825 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1826 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1827 // of -2^4, 2^4 * 32767/32768.
1828 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1829 if (!cellStateOut)
1830 {
1831 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1832 }
1833
1834 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1835 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1836 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1837 if (!output)
1838 {
1839 return Fail("%s: Could not read output 1: output", __func__);
1840 }
1841
1842 // Inputs
1843 const TensorInfo& inputInfo = input.GetTensorInfo();
1844 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1845 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1846
1847 // Outputs
1848 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1849 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1850
1851 // Dynamic tensors currently not supported
1852 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1853 {
1854 return Fail("%s: Dynamic output tensors are not supported", __func__);
1855 }
1856
1857 QuantizedLstmInputParams params;
1858
1859 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1860 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1861 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1862 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1863 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1864 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1865 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1866 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1867 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1868 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1869 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1870 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1871
1872 QuantizedLstmInputParamsInfo paramsInfo;
1873 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1874 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1875 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1876 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1877 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1878 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1879 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1880 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1881 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1882 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1883 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1884 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1885
1886 bool isSupported = false;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001887 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1888 {
1889 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1890 IsQuantizedLstmSupported,
1891 data.m_Backends,
1892 isSupported,
1893 inputInfo,
1894 previousCellStateInInfo,
1895 previousOutputInInfo,
1896 cellStateOutInfo,
1897 outputInfo,
1898 paramsInfo);
1899 };
1900
1901 bool isDynamic = false;
1902 if (!IsDynamicTensor(cellStateOutInfo) &&
1903 !IsDynamicTensor(outputInfo))
1904 {
1905 validateFunc(outputInfo, isSupported);
1906 }
1907 else
1908 {
1909 isDynamic = true;
1910 isSupported = AreDynamicTensorsSupported();
1911 }
Kevin May42477c12020-03-26 13:34:14 +00001912
1913 if (!isSupported)
1914 {
1915 return false;
1916 }
1917
1918 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1919 input.Connect(layer->GetInputSlot(0));
1920 previousCellStateIn.Connect(layer->GetInputSlot(1));
1921 previousOutputIn.Connect(layer->GetInputSlot(2));
1922
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001923 if (!isDynamic)
1924 {
1925 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1926 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
1927 }
1928 else
1929 {
1930 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1931 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01001932 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001933 }
1934
Kevin May42477c12020-03-26 13:34:14 +00001935}
1936
1937template<typename HalPolicy,
1938 typename HalOperation = typename HalPolicy::Operation,
1939 typename HalModel = typename HalPolicy::Model>
1940bool ConvertResize(const HalOperation& operation,
1941 const HalModel& model,
1942 ConversionData& data,
1943 ResizeMethod resizeMethod)
1944{
1945 using HalOperand = typename HalPolicy::Operand;
1946 using HalOperandType = typename HalPolicy::OperandType;
1947 ALOGV("HalPolicy::ConvertResize()");
1948 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
1949
1950 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1951 if (!input.IsValid())
1952 {
1953 return Fail("%s: Could not read input 0", __func__);
1954 }
1955
1956 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1957 if (!output)
1958 {
1959 return Fail("%s: Could not read output 0", __func__);
1960 }
1961
1962 const TensorInfo& inputInfo = input.GetTensorInfo();
1963 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1964
Kevin May42477c12020-03-26 13:34:14 +00001965 ResizeDescriptor descriptor;
1966 descriptor.m_Method = resizeMethod;
1967 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
1968
1969 HalOperandType operandType1;
1970 HalOperandType operandType2;
1971
1972 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
1973 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1974 {
1975 return Fail("%s: Operation has invalid inputs", __func__);
1976 }
1977
1978 if (operandType1 != operandType2)
1979 {
1980 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1981 }
1982
1983 if (operandType1 == HalOperandType::INT32)
1984 {
1985 // Case 1: resizing by shape
1986 int32_t targetWidth = 0;
1987 int32_t targetHeight = 0;
1988
1989 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
1990 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
1991 {
1992 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1993 }
1994
1995 if (targetWidth < 0 || targetHeight < 0)
1996 {
1997 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1998 "Target width/height cannot be < 0", __func__);
1999 }
2000
2001 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
2002 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
2003 }
2004 else if (operandType1 == HalOperandType::FLOAT32)
2005 {
2006 // Case 2: resizing by scale
2007 float widthScale = 1.0f;
2008 float heightScale = 1.0f;
2009
2010 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2011 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2012 {
2013 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2014 }
2015
2016 const TensorShape& inputShape = inputInfo.GetShape();
2017 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2018
2019 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2020 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2021
2022 descriptor.m_TargetWidth = std::floor(width * widthScale);
2023 descriptor.m_TargetHeight = std::floor(height * heightScale);
2024 }
2025 else if (operandType1 == HalOperandType::FLOAT16)
2026 {
2027 Half widthScale;
2028 Half heightScale;
2029
2030 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2031 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2032 {
2033 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2034 }
2035
2036 const TensorShape& inputShape = inputInfo.GetShape();
2037 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2038
2039 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2040 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2041
2042 descriptor.m_TargetWidth = std::floor(width * widthScale);
2043 descriptor.m_TargetHeight = std::floor(height * heightScale);
2044 }
2045 else
2046 {
2047 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2048 }
2049
David Monahanf057e6f2020-06-22 09:55:23 +01002050 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2051 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002052
Kevin May42477c12020-03-26 13:34:14 +00002053 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002054 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2055 {
2056 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2057 IsResizeSupported,
2058 data.m_Backends,
2059 isSupported,
2060 inputInfo,
2061 outputInfo,
2062 descriptor);
2063 };
2064
2065 if(IsDynamicTensor(outputInfo))
2066 {
2067 isSupported = AreDynamicTensorsSupported();
2068 }
2069 else
2070 {
2071 validateFunc(outputInfo, isSupported);
2072 }
Kevin May42477c12020-03-26 13:34:14 +00002073
2074 if (!isSupported)
2075 {
2076 return false;
2077 }
2078
2079 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Kevin May42477c12020-03-26 13:34:14 +00002080 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +00002081 input.Connect(layer->GetInputSlot(0));
2082
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002083 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002084}
2085
2086template<typename HalPolicy,
2087 typename HalOperation = typename HalPolicy::Operation,
2088 typename HalModel = typename HalPolicy::Model>
2089bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2090{
2091 using HalOperand = typename HalPolicy::Operand;
2092 using HalOperandType = typename HalPolicy::OperandType;
2093
2094 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2095
2096 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2097 if (!input.IsValid() )
2098 {
2099 return Fail("%s: Operation has invalid inputs", __func__);
2100 }
2101
2102 const TensorInfo& inputInfo = input.GetTensorInfo();
2103 unsigned int rank = inputInfo.GetNumDimensions();
2104 if (rank != 4)
2105 {
2106 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2107 }
2108
2109 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2110 if (!output)
2111 {
2112 return Fail("%s: Could not read output 0", __func__);
2113 }
2114
2115 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002116
2117 SpaceToDepthDescriptor desc;
2118
2119 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2120
2121 if (desc.m_BlockSize <= 1)
2122 {
2123 return Fail("%s: Block size must be at least 1 in all dimensions");
2124 }
2125
2126 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2127
2128 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002129 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2130 {
2131 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2132 IsSpaceToDepthSupported,
2133 data.m_Backends,
2134 isSupported,
2135 inputInfo,
2136 outputInfo,
2137 desc);
2138 };
2139
2140 if(IsDynamicTensor(outputInfo))
2141 {
2142 isSupported = AreDynamicTensorsSupported();
2143 }
2144 else
2145 {
2146 validateFunc(outputInfo, isSupported);
2147 }
2148
Kevin May42477c12020-03-26 13:34:14 +00002149 if (!isSupported)
2150 {
2151 return false;
2152 }
2153
2154 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
2155 assert(layer != nullptr);
2156 input.Connect(layer->GetInputSlot(0));
2157
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002158 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002159}
2160
2161template<typename HalPolicy,
2162 typename HalOperation = typename HalPolicy::Operation,
2163 typename HalModel = typename HalPolicy::Model>
2164bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2165{
2166 using HalOperand = typename HalPolicy::Operand;
2167 using HalOperandType = typename HalPolicy::OperandType;
2168
2169 ALOGV("HalPolicy::ConvertSoftmax()");
2170
2171 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2172 if (!input.IsValid())
2173 {
2174 return Fail("%s: Operation has invalid inputs", __func__);
2175 }
2176
2177 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2178 if (!outputOperand)
2179 {
2180 return Fail("%s: Operation has no outputs", __func__);
2181 }
2182
2183 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002184
2185 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002186 HalOperandType outputType = outputOperand->type;
2187
2188 // Read beta value
2189 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002190 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002191 Half value;
2192
2193 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2194 {
2195 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2196 }
2197
2198 desc.m_Beta = static_cast<float>(value);
2199 }
2200 else
2201 {
2202 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2203 {
2204 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2205 }
Kevin May42477c12020-03-26 13:34:14 +00002206 }
2207
2208 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002209 2,
2210 HalOperandType::INT32,
2211 desc.m_Axis,
2212 model,
2213 data))
Kevin May42477c12020-03-26 13:34:14 +00002214 {
2215 return Fail("%s: Operation has invalid inputs", __func__);
2216 }
2217
Kevin May42477c12020-03-26 13:34:14 +00002218 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002219 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2220 {
2221 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2222 IsSoftmaxSupported,
2223 data.m_Backends,
2224 isSupported,
2225 input.GetTensorInfo(),
2226 outputInfo,
2227 desc);
2228 };
2229
2230 if(IsDynamicTensor(outputInfo))
2231 {
2232 isSupported = AreDynamicTensorsSupported();
2233 }
2234 else
2235 {
2236 validateFunc(outputInfo, isSupported);
2237 }
2238
Kevin May42477c12020-03-26 13:34:14 +00002239 if (!isSupported)
2240 {
2241 return false;
2242 }
2243
2244 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
2245 assert(layer != nullptr);
2246 input.Connect(layer->GetInputSlot(0));
2247
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002248 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002249}
2250
2251template<typename HalPolicy,
2252 typename HalOperation = typename HalPolicy::Operation,
2253 typename HalModel = typename HalPolicy::Model>
2254bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2255{
2256 using HalOperand = typename HalPolicy::Operand;
2257 using HalOperandType = typename HalPolicy::OperandType;
2258
2259 ALOGV("HalPolicy::ConvertLstm()");
2260
2261 // Inputs:
2262 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2263 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2264 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2265 if (!input.IsValid())
2266 {
2267 return Fail("%s: Could not read input 0: input", __func__);
2268 }
2269 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2270 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2271 if (!outputStateIn.IsValid())
2272 {
2273 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2274 }
2275 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2276 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2277 if (!cellStateIn.IsValid())
2278 {
2279 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2280 }
2281
2282 // Get the mandatory input tensors:
2283 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2284 // [num_units, input_size].
2285 const ConstTensorPin inputToForgetWeightsPin =
2286 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2287 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2288 // [num_units, input_size].
2289 const ConstTensorPin inputToCellWeightsPin =
2290 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2291 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2292 // [num_units, input_size].
2293 const ConstTensorPin inputToOutputWeightsPin =
2294 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2295 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2296 // [num_units, output_size].
2297 const ConstTensorPin recurrentToForgetWeightsPin =
2298 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2299 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2300 // [num_units, output_size].
2301 const ConstTensorPin recurrentToCellWeightsPin =
2302 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2303 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2304 // [num_units, output_size].
2305 const ConstTensorPin recurrentToOutputWeightsPin =
2306 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2307 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2308 const ConstTensorPin forgetGateBiasPin =
2309 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2310 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2311 const ConstTensorPin cellBiasPin =
2312 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2313 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2314 const ConstTensorPin outputGateBiasPin =
2315 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2316
2317 if (!inputToForgetWeightsPin.IsValid() ||
2318 !inputToCellWeightsPin.IsValid() ||
2319 !inputToOutputWeightsPin.IsValid() ||
2320 !recurrentToForgetWeightsPin.IsValid() ||
2321 !recurrentToCellWeightsPin.IsValid() ||
2322 !recurrentToOutputWeightsPin.IsValid() ||
2323 !forgetGateBiasPin.IsValid() ||
2324 !cellBiasPin.IsValid() ||
2325 !outputGateBiasPin.IsValid())
2326 {
2327 return Fail("%s: Operation has invalid tensor inputs", __func__);
2328 }
2329
2330 // Get the optional input tensors:
2331 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2332 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2333 const ConstTensorPin inputToInputWeightsPin =
2334 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2335 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2336 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2337 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2338 const ConstTensorPin recurrentToInputWeightsPin =
2339 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2340 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2341 const ConstTensorPin cellToInputWeightsPin =
2342 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2343 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2344 const ConstTensorPin cellToForgetWeightsPin =
2345 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2346 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2347 const ConstTensorPin cellToOutputWeightsPin =
2348 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2349 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2350 const ConstTensorPin inputGateBiasPin =
2351 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2352 12,
2353 model,
2354 data,
2355 g_DontPermute,
2356 nullptr,
2357 true);
2358
2359 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2360 // [output_size, num_units].
2361 const ConstTensorPin projectionWeightsPin =
2362 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2363 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2364 const ConstTensorPin projectionBiasPin =
2365 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2366 17,
2367 model,
2368 data,
2369 g_DontPermute,
2370 nullptr,
2371 true);
2372
2373 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2374 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2375 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2376 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2377 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2378 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2379 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2380 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2381 {
2382 return Fail("%s: Operation has invalid tensor inputs", __func__);
2383 }
2384
2385 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2386 // 20: The activation function: A value indicating the activation function:
2387 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2388 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2389 // If set to 0.0 then clipping is disabled.
2390 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2391 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2392 ActivationFn activation;
2393 float cellClip;
2394 float projClip;
2395 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2396 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2397 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2398 {
2399 return Fail("%s: Operation has invalid scalar inputs", __func__);
2400 }
2401
2402 // Get the normalization tensors
2403 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2404 // Used to rescale normalized inputs to activation at input gate.
2405 const ConstTensorPin inputLayerNormWeightsPin
2406 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2407
2408 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2409 // Used to rescale normalized inputs to activation at forget gate.
2410 const ConstTensorPin forgetLayerNormWeightsPin =
2411 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2412 24,
2413 model,
2414 data,
2415 g_DontPermute,
2416 nullptr,
2417 true);
2418
2419 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2420 // Used to rescale normalized inputs to activation at cell gate.
2421 const ConstTensorPin cellLayerNormWeightsPin =
2422 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2423 25,
2424 model,
2425 data,
2426 g_DontPermute,
2427 nullptr,
2428 true);
2429
2430 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2431 // Used to rescale normalized inputs to activation at output gate.
2432 const ConstTensorPin outputLayerNormWeightsPin =
2433 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2434 26,
2435 model,
2436 data,
2437 g_DontPermute,
2438 nullptr,
2439 true);
2440
2441 // Outputs:
2442 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2443 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2444 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2445 if (!scratchBuffer)
2446 {
2447 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2448 }
2449 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2450 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2451 if (!outputStateOut)
2452 {
2453 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2454 }
2455 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2456 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2457 if (!cellStateOut)
2458 {
2459 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2460 }
2461 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2462 // effectively the same as the current “output state (out)” value.
2463 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2464 if (!output)
2465 {
2466 return Fail("%s: Could not read output 3: output", __func__);
2467 }
2468
2469 // set the params structure for the AddLstmLayer call
2470 LstmInputParams params;
2471 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2472 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2473 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2474 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2475 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2476 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2477 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2478 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2479 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2480 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2481 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2482 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2483 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2484 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2485 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2486 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2487 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2488 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2489 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2490 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2491 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2492
2493 // set the layer descriptor
2494 LstmDescriptor desc;
2495 desc.m_ActivationFunc = activation;
2496 desc.m_ClippingThresCell = cellClip;
2497 desc.m_ClippingThresProj = projClip;
2498 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2499 params.m_RecurrentToInputWeights == nullptr ||
2500 params.m_InputGateBias == nullptr);
2501 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2502 params.m_CellToOutputWeights != nullptr);
2503 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2504 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2505 params.m_ForgetLayerNormWeights != nullptr ||
2506 params.m_CellLayerNormWeights != nullptr ||
2507 params.m_OutputLayerNormWeights != nullptr);
2508
2509 // validate the optional input groups
2510 if (desc.m_CifgEnabled &&
2511 (params.m_InputToInputWeights != nullptr ||
2512 params.m_RecurrentToInputWeights != nullptr ||
2513 params.m_InputGateBias != nullptr))
2514 {
2515 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2516 " and input gate bias must be provided", __func__);
2517 }
2518
2519 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2520 {
2521 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2522 }
2523
2524 if (desc.m_PeepholeEnabled &&
2525 (params.m_CellToForgetWeights == nullptr ||
2526 params.m_CellToOutputWeights == nullptr ||
2527 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2528 {
2529 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2530 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2531 }
2532
2533 if (desc.m_LayerNormEnabled &&
2534 (params.m_ForgetLayerNormWeights == nullptr ||
2535 params.m_CellLayerNormWeights == nullptr ||
2536 params.m_OutputLayerNormWeights == nullptr ||
2537 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2538 {
2539 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2540 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2541 }
2542
2543 // Check if the layer is supported
2544 // Inputs
2545 const TensorInfo& inputInfo = input.GetTensorInfo();
2546 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2547 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2548
2549 // Outputs
2550 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2551 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2552 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2553 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2554
Kevin May42477c12020-03-26 13:34:14 +00002555 // Basic parameters
2556 LstmInputParamsInfo paramsInfo;
2557 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2558 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2559 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2560 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2561 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2562 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2563 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2564 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2565 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2566
2567 // Optional parameters
2568 if (!desc.m_CifgEnabled)
2569 {
2570 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2571 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2572 if (params.m_CellToInputWeights != nullptr)
2573 {
2574 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2575 }
2576 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2577 }
2578
2579 if (desc.m_ProjectionEnabled)
2580 {
2581 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2582 if (params.m_ProjectionBias != nullptr)
2583 {
2584 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2585 }
2586 }
2587
2588 if (desc.m_PeepholeEnabled)
2589 {
2590 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2591 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2592 }
2593
2594 if (desc.m_LayerNormEnabled)
2595 {
2596 if(!desc.m_CifgEnabled)
2597 {
2598 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2599 }
2600 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2601 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2602 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2603 }
2604
2605 bool isSupported = false;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002606 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2607 {
2608 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2609 IsLstmSupported,
2610 data.m_Backends,
2611 isSupported,
2612 inputInfo,
2613 outputStateInInfo,
2614 cellStateInInfo,
2615 scratchBufferInfo,
2616 outputStateOutInfo,
2617 cellStateOutInfo,
2618 outputInfo,
2619 desc,
2620 paramsInfo);
2621 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002622
Sadik Armagandbda4b72020-09-03 11:33:07 +01002623 bool isDynamic = false;
2624 if (!IsDynamicTensor(outputStateOutInfo) &&
2625 !IsDynamicTensor(scratchBufferInfo) &&
2626 !IsDynamicTensor(cellStateOutInfo) &&
2627 !IsDynamicTensor(outputInfo))
2628 {
2629 validateFunc(outputInfo, isSupported);
2630 }
2631 else
2632 {
2633 isDynamic = true;
2634 isSupported = AreDynamicTensorsSupported();
2635 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002636
Kevin May42477c12020-03-26 13:34:14 +00002637 if (!isSupported)
2638 {
2639 return false;
2640 }
2641
2642 // Add the layer
2643 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2644
2645 input.Connect(layer->GetInputSlot(0));
2646 outputStateIn.Connect(layer->GetInputSlot(1));
2647 cellStateIn.Connect(layer->GetInputSlot(2));
2648
Sadik Armagandbda4b72020-09-03 11:33:07 +01002649 if (!isDynamic)
2650 {
2651 return (
2652 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2653 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2654 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2655 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2656 }
2657 else
2658 {
2659 return (
2660 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2661 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2662 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2663 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002664 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002665 }
2666
Kevin May42477c12020-03-26 13:34:14 +00002667}
2668
2669template<typename HalPolicy,
2670 typename HalOperation = typename HalPolicy::Operation,
2671 typename HalModel = typename HalPolicy::Model>
2672bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2673{
2674 using HalOperand = typename HalPolicy::Operand;
2675 using HalOperandType = typename HalPolicy::OperandType;
2676
2677 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2678
2679 if (!input.IsValid())
2680 {
2681 return Fail("%s: Operation has invalid inputs", __func__);
2682 }
2683
2684 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2685
2686 if (!output)
2687 {
2688 return Fail("%s: Could not read output 0", __func__);
2689 }
2690
2691 const TensorInfo& inputInfo = input.GetTensorInfo();
2692 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002693
2694 // ArmNN does not currently support non-fixed weights or bias
2695 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2696 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2697
2698 if (weightsOperand == nullptr)
2699 {
2700 return Fail("%s: Operand is invalid", __func__);
2701 }
2702 TransposeConvolution2dDescriptor desc;
2703 desc.m_DataLayout = DataLayout::NHWC;
2704
2705 // Determine whether padding is implicit or explicit
2706 bool implicitPadding = operation.inputs.size() == 9;
2707
2708 if (implicitPadding )
2709 {
2710 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2711 }
2712 else
2713 {
2714 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2715 }
2716
2717 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2718 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2719 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2720
2721 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2722
2723 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2724 // We have to permute it to OIHW if the data layout is NCHW.
2725 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2726 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2727 model, data, OHWIToOIHW) :
2728 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2729
2730 // Bias is a 1D tensor
2731 const ConstTensorPin biasPin =
2732 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2733
2734 if (!weightsPin.IsValid())
2735 {
2736 return Fail("%s: Operation has invalid weights", __func__);
2737 }
2738
2739 if (!biasPin.IsValid())
2740 {
2741 return Fail("%s: Operation has invalid biases", __func__);
2742 }
2743
2744 ConstTensor weights = weightsPin.GetConstTensor();
2745 ConstTensor bias = biasPin.GetConstTensor();
2746 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2747
2748 ActivationFn activation;
2749
2750 if (implicitPadding)
2751 {
2752 int32_t strideX{0};
2753 int32_t strideY{0};
2754 int32_t padLeft{0};
2755 int32_t padRight{0};
2756 int32_t padTop{0};
2757 int32_t padBottom{0};
2758
2759 android::nn::PaddingScheme paddingScheme;
2760 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2761 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2762 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2763 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2764 {
2765 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2766 }
2767
2768 const uint32_t kernelX = weights.GetShape()[widthIndex];
2769 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00002770
Colm Donelan8f3d33e2020-07-06 15:41:43 +01002771 // If output shape has been specified as a parameter then extract it and make it available.
2772 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
2773 std::vector<int32_t> outputShape;
2774 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
2775 {
2776 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
2777 for (int dimension : outputShape)
2778 {
2779 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
2780 }
2781 desc.m_OutputShapeEnabled = true;
2782 }
2783
Finn Williams8fe50c62020-10-09 15:52:57 +01002784 uint32_t outputX;
2785 uint32_t outputY;
2786
2787 if (IsDynamicTensor(outputInfo))
2788 {
2789 if (outputShape.size() == 0)
2790 {
2791 return Fail("%s: Padding sizes cannot be inferred", __func__);
2792 }
2793
2794 outputX = outputShape[widthIndex];
2795 outputY = outputShape[heightIndex];
2796 }
2797 else
2798 {
2799 outputX = outputInfo.GetShape()[widthIndex];
2800 outputY = outputInfo.GetShape()[heightIndex];
2801 }
2802
2803 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2804 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
2805
2806 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2807 // but Arm NN only supports values >= 0
2808 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2809 {
2810 return Fail("%s: Negative padding values are not supported", __func__);
2811 }
2812
Matthew Sloyan9b088d92020-09-14 15:12:55 +01002813 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
2814 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
2815 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
2816 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
2817 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
2818 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00002819 }
2820 else if (operation.inputs.size() == 11)
2821 {
2822 // explicit padding
2823 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2824 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2825 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2826 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2827 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2828 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
2829 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
2830 {
2831 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2832 }
2833 }
2834 else
2835 {
2836 return Fail("%s: Unsupported number of operation inputs", __func__);
2837 }
2838
2839 desc.m_BiasEnabled = true;
2840 Optional<TensorInfo> biases(bias.GetInfo());
2841
2842 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002843 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2844 {
2845 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2846 IsTransposeConvolution2dSupported,
2847 data.m_Backends,
2848 isSupported,
2849 inputInfo,
2850 outputInfo,
2851 desc,
2852 weights.GetInfo(),
2853 biases);
2854 };
2855
2856 if(IsDynamicTensor(outputInfo))
2857 {
2858 isSupported = AreDynamicTensorsSupported();
2859 }
2860 else
2861 {
2862 validateFunc(outputInfo, isSupported);
2863 }
Kevin May42477c12020-03-26 13:34:14 +00002864 if (!isSupported)
2865 {
2866 return false;
2867 }
2868
2869 IConnectableLayer* startLayer =
2870 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
2871 if (!startLayer)
2872 {
2873 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2874 }
2875
Kevin May42477c12020-03-26 13:34:14 +00002876 input.Connect(startLayer->GetInputSlot(0));
2877
Kevin Mayfcf2a152020-09-08 16:06:32 +01002878 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
2879 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00002880}
2881
2882} // armnn_driver namespace