blob: e4bc05f98e90092648af28a28a1c95aaa2f3e7c1 [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 }
808 const 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
899 const unsigned int outputChannels = outputShape[channelsIndex];
900
901 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
902 const unsigned int channelMultiplier = outputChannels / numGroups;
903
904 //
905 // Validate all relevant inputs
906 //
907 if (numGroups <= 0)
908 {
909 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
910 }
911
912 if (outputChannels % numGroups != 0u)
913 {
914 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
915 }
916
917 //
918 // Set up Splitter layer
919 //
920 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
921 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
922
923 TensorInfo splitterOutputInfo(4,
924 splitterDimSizes,
925 inputInfo.GetDataType(),
926 inputInfo.GetQuantizationScale(),
927 inputInfo.GetQuantizationOffset());
928
929 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
930
931 ViewsDescriptor splitterDesc(numGroups);
932 for (unsigned int group = 0u; group < numGroups; ++group)
933 {
934 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
935 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
936 {
937 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
938 }
939 }
940
941 bool isSupported = false;
942 FORWARD_LAYER_SUPPORT_FUNC(__func__,
943 IsSplitterSupported,
944 data.m_Backends,
945 isSupported,
946 inputInfo,
947 splitterOutputInfos,
948 splitterDesc);
949 if (!isSupported)
950 {
951 return false;
952 }
953
954 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
955 if (!splitterLayer)
956 {
957 return Fail("%s: Failed to add SplitterLayer", __func__);
958 }
959
960 input.Connect(splitterLayer->GetInputSlot(0));
961 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
962 {
963 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
964 }
965
966 //
967 // Set up Convolution2d layers for each group
968 //
969
970 // Set up group tensor shapes
971 TensorShape groupInputShape(inputShape);
972 groupInputShape[channelsIndex] = channelsPerGroup;
973
974 TensorShape groupOutputShape(outputShape);
975 groupOutputShape[channelsIndex] = 1;
976
977 TensorShape groupWeightsShape(weightsShape);
978 groupWeightsShape[0] /= channelMultiplier * numGroups;
979
980 TensorShape groupBiasesShape({ 1 });
981
982 // Set up group tensor infos
983 TensorInfo groupInputInfo(inputInfo);
984 groupInputInfo.SetShape(groupInputShape);
985
986 const TensorInfo& weightsInfo = weights.GetInfo();
987 TensorInfo groupWeightsInfo(weightsInfo);
988 groupWeightsInfo.SetShape(groupWeightsShape);
989
990 const TensorInfo& biasesInfo = biases.GetInfo();
991 TensorInfo groupBiasesInfo(biasesInfo);
992 groupBiasesInfo.SetShape(groupBiasesShape);
993
994 TensorInfo groupOutputInfo(outputInfo);
995 groupOutputInfo.SetShape(groupOutputShape);
996
997 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
998 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
999
1000 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1001 for (unsigned int group = 0u; group < numGroups; ++group)
1002 {
1003 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1004 {
1005 auto index = group * channelMultiplier + m;
1006
1007 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1008 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1009
1010 if (weightsInfo.HasPerAxisQuantization())
1011 {
1012 // Extract per-axis quantization scales for group weights
1013 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1014 groupWeightsInfo.SetQuantizationScales(
1015 std::vector<float>(weightsQuantScales.begin() + index,
1016 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1017
1018 // Extract per-axis quantization scales for group biases
1019 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1020 groupBiasesInfo.SetQuantizationScales(
1021 std::vector<float>(biasesQuantScales.begin() + index,
1022 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1023 }
1024
1025 // Extract weights and biases data for current group convolution
1026 ConstTensor groupWeights(groupWeightsInfo,
1027 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1028 weightsDataOffset));
1029 ConstTensor groupBiases(groupBiasesInfo,
1030 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1031 biasesDataOffset));
1032
1033 isSupported = false;
1034 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1035 IsConvolution2dSupported,
1036 data.m_Backends,
1037 isSupported,
1038 groupInputInfo,
1039 groupOutputInfo,
1040 desc,
1041 groupWeightsInfo,
1042 Optional<TensorInfo>(groupBiasesInfo));
1043 if (!isSupported)
1044 {
1045 return false;
1046 }
1047
1048 IConnectableLayer* convLayer =
1049 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1050 if (!convLayer)
1051 {
1052 return Fail("%s: AddConvolution2dLayer failed", __func__);
1053 }
1054
1055 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1056 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1057
1058 convLayers[index] = convLayer;
1059 }
1060 }
1061
1062 //
1063 // Set up Concat layer
1064 //
1065 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
1066 for (unsigned int group = 0u; group < numGroups; ++group)
1067 {
1068 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1069 {
1070 auto index = group * channelMultiplier + m;
1071 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1072 concatDescriptor.SetConcatAxis(channelsIndex);
1073 }
1074 }
1075
1076 isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001077 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1078 {
1079 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1080 IsConcatSupported,
1081 data.m_Backends,
1082 isSupported,
1083 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1084 outputInfo,
1085 concatDescriptor);
1086 };
1087
1088 if(!IsDynamicTensor(outputInfo))
1089 {
1090 validateFunc(outputInfo, isSupported);
1091 }
1092 else
1093 {
1094 isSupported = AreDynamicTensorsSupported();
1095 }
1096
Kevin May42477c12020-03-26 13:34:14 +00001097 if (!isSupported)
1098 {
1099 return false;
1100 }
1101
1102 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1103 if (!concatLayer)
1104 {
1105 return Fail("%s: AddConcatLayer failed", __func__);
1106 }
1107
1108 for (unsigned int group = 0u; group < numGroups; ++group)
1109 {
1110 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1111 {
1112 auto index = group * channelMultiplier + m;
1113 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1114 }
1115 }
1116 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1117
Kevin Mayfcf2a152020-09-08 16:06:32 +01001118 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
1119 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00001120}
1121
1122template<typename HalPolicy,
1123 typename HalOperation = typename HalPolicy::Operation,
1124 typename HalModel = typename HalPolicy::Model>
1125bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1126{
1127 using HalOperand = typename HalPolicy::Operand;
1128 using HalOperandType = typename HalPolicy::OperandType;
1129
1130 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1131
1132 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1133 if (!input.IsValid())
1134 {
1135 return Fail("%s: Operation has an invalid input 0", __func__);
1136 }
1137
1138 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1139 if (!output)
1140 {
1141 return Fail("%s: Operation has an invalid output", __func__);
1142 }
1143
1144 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001145
1146 // Determine data type of input tensor
1147 HalOperandType inputType;
1148 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1149 {
1150 return Fail("%s: Operation has invalid inputs", __func__);
1151 }
1152
1153 InstanceNormalizationDescriptor desc;
1154
1155 // Read gamma, beta & epsilon
1156 if (inputType == HalOperandType::TENSOR_FLOAT16)
1157 {
1158 Half fp16Gamma;
1159 Half fp16Beta;
1160 Half fp16Epsilon;
1161
1162 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1163 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1164 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1165 {
1166 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1167 }
1168
1169 desc.m_Gamma = static_cast<float>(fp16Gamma);
1170 desc.m_Beta = static_cast<float>(fp16Beta);
1171 desc.m_Eps = static_cast<float>(fp16Epsilon);
1172 }
1173 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1174 {
1175 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1176 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1177 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1178 {
1179 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1180 }
1181 }
1182 else
1183 {
1184 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1185 }
1186
1187 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1188
1189 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001190 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1191 {
1192 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1193 IsInstanceNormalizationSupported,
1194 data.m_Backends,
1195 isSupported,
1196 input.GetTensorInfo(),
1197 outputInfo,
1198 desc);
1199 };
1200
1201 if(IsDynamicTensor(outputInfo))
1202 {
1203 isSupported = AreDynamicTensorsSupported();
1204 }
1205 else
1206 {
1207 validateFunc(outputInfo, isSupported);
1208 }
1209
Kevin May42477c12020-03-26 13:34:14 +00001210 if (!isSupported)
1211 {
1212 return false;
1213 }
1214
1215 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1216 input.Connect(layer->GetInputSlot(0));
1217
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001218 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001219}
1220
1221template<typename HalPolicy,
1222 typename HalOperation = typename HalPolicy::Operation,
1223 typename HalModel = typename HalPolicy::Model>
1224bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1225{
1226 using HalOperand = typename HalPolicy::Operand;
1227 using HalOperandType = typename HalPolicy::OperandType;
1228
1229 ALOGV("HalPolicy::ConvertLogSoftmax()");
1230
1231 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1232 if (!input.IsValid())
1233 {
1234 return Fail("%s: Failed to read input 0", __func__);
1235 }
1236
1237 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1238 if (!output)
1239 {
1240 return Fail("%s: Failed to read output", __func__);
1241 }
1242
1243 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001244
1245 // Determine data type of input tensor
1246 HalOperandType inputType;
1247 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1248 {
1249 return Fail("%s: Operation has invalid inputs", __func__);
1250 }
1251
1252 LogSoftmaxDescriptor descriptor;
1253
1254 // Read beta
1255 if (inputType == HalOperandType::TENSOR_FLOAT16)
1256 {
1257 Half fp16Beta;
1258 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1259 {
1260 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1261 }
1262
1263 descriptor.m_Beta = static_cast<float>(fp16Beta);
1264 }
1265 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1266 {
1267 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1268 {
1269 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1270 }
1271 }
1272 else
1273 {
1274 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1275 }
1276
1277 // Read axis
1278 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1279 {
1280 return Fail("%s: Failed to read input 2", __func__);
1281 }
1282
1283 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001284 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1285 {
1286 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1287 IsLogSoftmaxSupported,
1288 data.m_Backends,
1289 isSupported,
1290 input.GetTensorInfo(),
1291 outputInfo,
1292 descriptor);
1293 };
1294
1295 if(IsDynamicTensor(outputInfo))
1296 {
1297 isSupported = AreDynamicTensorsSupported();
1298 }
1299 else
1300 {
1301 validateFunc(outputInfo, isSupported);
1302 }
1303
Kevin May42477c12020-03-26 13:34:14 +00001304 if (!isSupported)
1305 {
1306 return false;
1307 }
1308
1309 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
1310 if (!layer)
1311 {
1312 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1313 }
1314
1315 input.Connect(layer->GetInputSlot(0));
1316
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001317 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001318}
1319
1320template<typename HalPolicy,
1321 typename HalOperation = typename HalPolicy::Operation,
1322 typename HalModel = typename HalPolicy::Model>
1323bool ConvertMaximum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1324{
1325 using HalOperand = typename HalPolicy::Operand;
1326
1327 ALOGV("HalPolicy::ConvertMaximum()");
1328
1329 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1330 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1331
1332 if (!input0.IsValid() || !input1.IsValid())
1333 {
1334 return Fail("%s: Operation has invalid inputs", __func__);
1335 }
1336
1337 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1338 if (!outputOperand)
1339 {
1340 return Fail("%s: Could not read output", __func__);
1341 }
1342
1343 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001344
1345 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001346 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
1347 {
1348 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1349 IsMaximumSupported,
1350 data.m_Backends,
1351 isSupported,
1352 input0.GetTensorInfo(),
1353 input1.GetTensorInfo(),
1354 outInfo);
1355 };
1356
1357 if(IsDynamicTensor(outInfo))
1358 {
1359 isSupported = AreDynamicTensorsSupported();
1360 }
1361 else
1362 {
1363 validateFunc(outInfo, isSupported);
1364 }
Kevin May42477c12020-03-26 13:34:14 +00001365
1366 if (!isSupported)
1367 {
1368 return false;
1369 }
1370
1371 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
1372 assert(layer != nullptr);
1373 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1374 if (!isReshapeSupported)
1375 {
1376 return false;
1377 }
1378
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001379 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001380}
1381
1382template<typename HalPolicy,
1383 typename HalOperation = typename HalPolicy::Operation,
1384 typename HalModel = typename HalPolicy::Model>
1385bool ConvertMinimum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1386{
1387 using HalOperand = typename HalPolicy::Operand;
1388
1389 ALOGV("HalPolicy::ConvertMinimum()");
1390
1391 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1392 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1393
1394 if (!input0.IsValid() || !input1.IsValid())
1395 {
1396 return Fail("%s: Operation has invalid inputs", __func__);
1397 }
1398
1399 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1400 if (!output)
1401 {
1402 return Fail("%s: Could not read output 0", __func__);
1403 }
1404
1405 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001406
1407 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001408 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1409 {
1410 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1411 IsMinimumSupported,
1412 data.m_Backends,
1413 isSupported,
1414 input0.GetTensorInfo(),
1415 input1.GetTensorInfo(),
1416 outputInfo);
1417 };
1418
1419 if(IsDynamicTensor(outputInfo))
1420 {
1421 isSupported = AreDynamicTensorsSupported();
1422 }
1423 else
1424 {
1425 validateFunc(outputInfo, isSupported);
1426 }
Kevin May42477c12020-03-26 13:34:14 +00001427
1428 if (!isSupported)
1429 {
1430 return false;
1431 }
1432
1433 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
1434 assert(layer != nullptr);
1435 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1436 if (!isReshapeSupported)
1437 {
1438 return false;
1439 }
1440
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001441 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001442}
1443
1444template<typename HalPolicy,
1445 typename HalOperation = typename HalPolicy::Operation,
1446 typename HalModel = typename HalPolicy::Model>
1447bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1448{
1449 using HalOperand = typename HalPolicy::Operand;
1450 using HalOperandType = typename HalPolicy::OperandType;
1451
1452 ALOGV("HalPolicy::ConvertPadV2()");
1453
1454 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1455 if (!input.IsValid())
1456 {
1457 return Fail("%s: Could not read input 0", __func__);
1458 }
1459
1460 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1461 if (!output)
1462 {
1463 return Fail("%s: Could not read output", __func__);
1464 }
1465
1466 const TensorInfo& inputInfo = input.GetTensorInfo();
1467 unsigned int rank = inputInfo.GetNumDimensions();
1468
1469 PadDescriptor descriptor;
1470 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1471 {
1472 return Fail("%s: Could not convert paddings", __func__);
1473 }
1474
1475 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001476
1477 // Determine type of padding value
1478 HalOperandType operandType0;
1479 HalOperandType operandType2;
1480
1481 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1482 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1483 {
1484 return Fail("%s: Operation has invalid inputs", __func__);
1485 }
1486
1487 // Read value to use for padding
1488 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1489 {
1490 Half f16PadValue;
1491 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1492 {
1493 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1494 }
1495
1496 descriptor.m_PadValue = f16PadValue;
1497 }
1498 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1499 {
1500 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1501 {
1502 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1503 }
1504 }
1505 else if (operandType0 == HalOperandType::TENSOR_QUANT8_ASYMM && operandType2 == HalOperandType::INT32)
1506 {
1507 int32_t intPadValue = 0;
1508 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1509 {
1510 return Fail("%s: Could not read input 2 (INT32)", __func__);
1511 }
1512 descriptor.m_PadValue = intPadValue;
1513 }
1514 else
1515 {
1516 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1517 }
1518
1519 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001520 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1521 {
1522 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1523 IsPadSupported,
1524 data.m_Backends,
1525 isSupported,
1526 inputInfo,
1527 outputInfo,
1528 descriptor);
1529 };
1530
1531 if(IsDynamicTensor(outputInfo))
1532 {
1533 isSupported = AreDynamicTensorsSupported();
1534 }
1535 else
1536 {
1537 validateFunc(outputInfo, isSupported);
1538 }
1539
Kevin May42477c12020-03-26 13:34:14 +00001540 if (!isSupported)
1541 {
1542 return false;
1543 }
1544
1545 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
1546 assert(layer != nullptr);
1547 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001548
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001549 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001550}
1551
1552template<typename HalPolicy,
1553 typename HalOperation = typename HalPolicy::Operation,
1554 typename HalModel = typename HalPolicy::Model>
1555bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1556{
1557 using HalOperand = typename HalPolicy::Operand;
1558
1559 ALOGV("HalPolicy::ConvertPrelu()");
1560
1561 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1562 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1563
1564 if (!input.IsValid() || !alpha.IsValid())
1565 {
1566 return Fail("%s: Operation has invalid inputs", __func__);
1567 }
1568
1569 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1570
1571 if (!output)
1572 {
1573 return Fail("%s: Could not read output", __func__);
1574 }
1575
1576 const TensorInfo& inputInfo = input.GetTensorInfo();
1577 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1578 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1579
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001580 bool isSupported = false;
1581 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001582 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001583 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1584 IsPreluSupported,
1585 data.m_Backends,
1586 isSupported,
1587 inputInfo,
1588 alphaInfo,
1589 outputInfo);
1590 };
1591
1592 if(IsDynamicTensor(outputInfo))
1593 {
1594 isSupported = AreDynamicTensorsSupported();
1595 }
1596 else
1597 {
1598 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001599 }
1600
Kevin May42477c12020-03-26 13:34:14 +00001601 if (!isSupported)
1602 {
1603 return false;
1604 }
1605
1606 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
1607
1608 if (!layer)
1609 {
1610 return Fail("%s: AddPreluLayer failed", __func__);
1611 }
1612
1613 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1614 if (!isReshapeSupported)
1615 {
1616 return false;
1617 }
1618
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001619 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001620}
1621
1622template<typename HalPolicy,
1623 typename HalOperation = typename HalPolicy::Operation,
1624 typename HalModel = typename HalPolicy::Model>
1625bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1626{
1627 using HalOperand = typename HalPolicy::Operand;
1628
1629 ALOGV("HalPolicy::ConvertQuantize()");
1630
1631 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1632 if (!input.IsValid())
1633 {
1634 return Fail("%s: Operation has invalid input", __func__);
1635 }
1636
1637 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1638 if (!outputOperand)
1639 {
1640 return Fail("%s: Operation has invalid outputs", __func__);
1641 }
1642
1643 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001644
1645 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001646 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1647 {
1648 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1649 IsQuantizeSupported,
1650 data.m_Backends,
1651 isSupported,
1652 input.GetTensorInfo(),
1653 outputInfo);
1654 };
1655
1656 if(IsDynamicTensor(outputInfo))
1657 {
1658 isSupported = AreDynamicTensorsSupported();
1659 }
1660 else
1661 {
1662 validateFunc(outputInfo, isSupported);
1663 }
1664
Kevin May42477c12020-03-26 13:34:14 +00001665 if (!isSupported)
1666 {
1667 return false;
1668 }
1669
1670 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
1671 assert(layer != nullptr);
1672 input.Connect(layer->GetInputSlot(0));
1673
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001674 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001675}
1676
1677template<typename HalPolicy,
1678 typename HalOperation = typename HalPolicy::Operation,
1679 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001680bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001681{
1682 using HalOperand = typename HalPolicy::Operand;
1683
Sadik Armagan813f2302020-05-19 14:10:30 +01001684 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001685
1686 //Inputs:
1687 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1688 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1689 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1690 if (!input.IsValid())
1691 {
1692 return Fail("%s: Could not read input 0: input", __func__);
1693 }
1694
1695 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1696 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1697 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1698 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1699 if (!previousCellStateIn.IsValid())
1700 {
1701 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1702 }
1703
1704 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1705 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1706 // is quantized with a fixed quantization range of -1, 127/128.
1707 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1708 if (!previousOutputIn.IsValid())
1709 {
1710 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1711 }
1712
1713 // Get the input tensors:
1714 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1715 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1716 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1717 const ConstTensorPin inputToInputWeightsPin =
1718 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1719
1720 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1721 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1722 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1723 const ConstTensorPin inputToForgetWeightsPin =
1724 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1725
1726 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1727 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1728 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1729 const ConstTensorPin inputToCellWeightsPin =
1730 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1731
1732 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1733 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1734 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1735 const ConstTensorPin inputToOutputWeightsPin =
1736 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1737
1738 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1739 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1740 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1741 const ConstTensorPin recurrentToInputWeightsPin =
1742 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1743
1744 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1745 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1746 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1747 const ConstTensorPin recurrentToForgetWeightsPin =
1748 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1749
1750 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1751 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1752 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1753 const ConstTensorPin recurrentToCellWeightsPin =
1754 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1755
1756 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1757 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1758 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1759 const ConstTensorPin recurrentToOutputWeightsPin =
1760 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1761
1762 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1763 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1764 // of input and weights scales and zeroPoint equal to 0.
1765 const ConstTensorPin inputGateBiasPin =
1766 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1767
1768 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1769 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1770 // of input and weights scales and zeroPoint equal to 0.
1771 const ConstTensorPin forgetGateBiasPin =
1772 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1773
1774 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1775 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1776 // and weights scales and zeroPoint equal to 0.
1777 const ConstTensorPin cellBiasPin =
1778 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1779
1780 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1781 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1782 // of input and weights scales and zeroPoint equal to 0.
1783 const ConstTensorPin outputGateBiasPin =
1784 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1785
1786 if (!inputToInputWeightsPin.IsValid() ||
1787 !inputToForgetWeightsPin.IsValid() ||
1788 !inputToCellWeightsPin.IsValid() ||
1789 !inputToOutputWeightsPin.IsValid() ||
1790 !recurrentToInputWeightsPin.IsValid() ||
1791 !recurrentToForgetWeightsPin.IsValid() ||
1792 !recurrentToCellWeightsPin.IsValid() ||
1793 !recurrentToOutputWeightsPin.IsValid() ||
1794 !inputGateBiasPin.IsValid() ||
1795 !forgetGateBiasPin.IsValid() ||
1796 !cellBiasPin.IsValid() ||
1797 !outputGateBiasPin.IsValid())
1798 {
1799 return Fail("%s: Operation has invalid tensor inputs", __func__);
1800 }
1801
1802 // Outputs:
1803 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1804 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1805 // of -2^4, 2^4 * 32767/32768.
1806 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1807 if (!cellStateOut)
1808 {
1809 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1810 }
1811
1812 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1813 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1814 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1815 if (!output)
1816 {
1817 return Fail("%s: Could not read output 1: output", __func__);
1818 }
1819
1820 // Inputs
1821 const TensorInfo& inputInfo = input.GetTensorInfo();
1822 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1823 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1824
1825 // Outputs
1826 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1827 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1828
1829 // Dynamic tensors currently not supported
1830 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1831 {
1832 return Fail("%s: Dynamic output tensors are not supported", __func__);
1833 }
1834
1835 QuantizedLstmInputParams params;
1836
1837 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1838 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1839 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1840 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1841 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1842 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1843 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1844 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1845 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1846 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1847 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1848 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1849
1850 QuantizedLstmInputParamsInfo paramsInfo;
1851 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1852 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1853 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1854 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1855 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1856 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1857 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1858 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1859 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1860 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1861 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1862 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1863
1864 bool isSupported = false;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001865 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1866 {
1867 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1868 IsQuantizedLstmSupported,
1869 data.m_Backends,
1870 isSupported,
1871 inputInfo,
1872 previousCellStateInInfo,
1873 previousOutputInInfo,
1874 cellStateOutInfo,
1875 outputInfo,
1876 paramsInfo);
1877 };
1878
1879 bool isDynamic = false;
1880 if (!IsDynamicTensor(cellStateOutInfo) &&
1881 !IsDynamicTensor(outputInfo))
1882 {
1883 validateFunc(outputInfo, isSupported);
1884 }
1885 else
1886 {
1887 isDynamic = true;
1888 isSupported = AreDynamicTensorsSupported();
1889 }
Kevin May42477c12020-03-26 13:34:14 +00001890
1891 if (!isSupported)
1892 {
1893 return false;
1894 }
1895
1896 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1897 input.Connect(layer->GetInputSlot(0));
1898 previousCellStateIn.Connect(layer->GetInputSlot(1));
1899 previousOutputIn.Connect(layer->GetInputSlot(2));
1900
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001901 if (!isDynamic)
1902 {
1903 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1904 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
1905 }
1906 else
1907 {
1908 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1909 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01001910 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001911 }
1912
Kevin May42477c12020-03-26 13:34:14 +00001913}
1914
1915template<typename HalPolicy,
1916 typename HalOperation = typename HalPolicy::Operation,
1917 typename HalModel = typename HalPolicy::Model>
1918bool ConvertResize(const HalOperation& operation,
1919 const HalModel& model,
1920 ConversionData& data,
1921 ResizeMethod resizeMethod)
1922{
1923 using HalOperand = typename HalPolicy::Operand;
1924 using HalOperandType = typename HalPolicy::OperandType;
1925 ALOGV("HalPolicy::ConvertResize()");
1926 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
1927
1928 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1929 if (!input.IsValid())
1930 {
1931 return Fail("%s: Could not read input 0", __func__);
1932 }
1933
1934 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1935 if (!output)
1936 {
1937 return Fail("%s: Could not read output 0", __func__);
1938 }
1939
1940 const TensorInfo& inputInfo = input.GetTensorInfo();
1941 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1942
Kevin May42477c12020-03-26 13:34:14 +00001943 ResizeDescriptor descriptor;
1944 descriptor.m_Method = resizeMethod;
1945 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
1946
1947 HalOperandType operandType1;
1948 HalOperandType operandType2;
1949
1950 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
1951 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1952 {
1953 return Fail("%s: Operation has invalid inputs", __func__);
1954 }
1955
1956 if (operandType1 != operandType2)
1957 {
1958 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1959 }
1960
1961 if (operandType1 == HalOperandType::INT32)
1962 {
1963 // Case 1: resizing by shape
1964 int32_t targetWidth = 0;
1965 int32_t targetHeight = 0;
1966
1967 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
1968 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
1969 {
1970 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1971 }
1972
1973 if (targetWidth < 0 || targetHeight < 0)
1974 {
1975 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1976 "Target width/height cannot be < 0", __func__);
1977 }
1978
1979 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
1980 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
1981 }
1982 else if (operandType1 == HalOperandType::FLOAT32)
1983 {
1984 // Case 2: resizing by scale
1985 float widthScale = 1.0f;
1986 float heightScale = 1.0f;
1987
1988 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
1989 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
1990 {
1991 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1992 }
1993
1994 const TensorShape& inputShape = inputInfo.GetShape();
1995 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1996
1997 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1998 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1999
2000 descriptor.m_TargetWidth = std::floor(width * widthScale);
2001 descriptor.m_TargetHeight = std::floor(height * heightScale);
2002 }
2003 else if (operandType1 == HalOperandType::FLOAT16)
2004 {
2005 Half widthScale;
2006 Half heightScale;
2007
2008 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2009 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2010 {
2011 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2012 }
2013
2014 const TensorShape& inputShape = inputInfo.GetShape();
2015 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2016
2017 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2018 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2019
2020 descriptor.m_TargetWidth = std::floor(width * widthScale);
2021 descriptor.m_TargetHeight = std::floor(height * heightScale);
2022 }
2023 else
2024 {
2025 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2026 }
2027
David Monahanf057e6f2020-06-22 09:55:23 +01002028 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2029 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002030
Kevin May42477c12020-03-26 13:34:14 +00002031 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002032 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2033 {
2034 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2035 IsResizeSupported,
2036 data.m_Backends,
2037 isSupported,
2038 inputInfo,
2039 outputInfo,
2040 descriptor);
2041 };
2042
2043 if(IsDynamicTensor(outputInfo))
2044 {
2045 isSupported = AreDynamicTensorsSupported();
2046 }
2047 else
2048 {
2049 validateFunc(outputInfo, isSupported);
2050 }
Kevin May42477c12020-03-26 13:34:14 +00002051
2052 if (!isSupported)
2053 {
2054 return false;
2055 }
2056
2057 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Kevin May42477c12020-03-26 13:34:14 +00002058 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +00002059 input.Connect(layer->GetInputSlot(0));
2060
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002061 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002062}
2063
2064template<typename HalPolicy,
2065 typename HalOperation = typename HalPolicy::Operation,
2066 typename HalModel = typename HalPolicy::Model>
2067bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2068{
2069 using HalOperand = typename HalPolicy::Operand;
2070 using HalOperandType = typename HalPolicy::OperandType;
2071
2072 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2073
2074 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2075 if (!input.IsValid() )
2076 {
2077 return Fail("%s: Operation has invalid inputs", __func__);
2078 }
2079
2080 const TensorInfo& inputInfo = input.GetTensorInfo();
2081 unsigned int rank = inputInfo.GetNumDimensions();
2082 if (rank != 4)
2083 {
2084 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2085 }
2086
2087 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2088 if (!output)
2089 {
2090 return Fail("%s: Could not read output 0", __func__);
2091 }
2092
2093 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002094
2095 SpaceToDepthDescriptor desc;
2096
2097 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2098
2099 if (desc.m_BlockSize <= 1)
2100 {
2101 return Fail("%s: Block size must be at least 1 in all dimensions");
2102 }
2103
2104 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2105
2106 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002107 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2108 {
2109 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2110 IsSpaceToDepthSupported,
2111 data.m_Backends,
2112 isSupported,
2113 inputInfo,
2114 outputInfo,
2115 desc);
2116 };
2117
2118 if(IsDynamicTensor(outputInfo))
2119 {
2120 isSupported = AreDynamicTensorsSupported();
2121 }
2122 else
2123 {
2124 validateFunc(outputInfo, isSupported);
2125 }
2126
Kevin May42477c12020-03-26 13:34:14 +00002127 if (!isSupported)
2128 {
2129 return false;
2130 }
2131
2132 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
2133 assert(layer != nullptr);
2134 input.Connect(layer->GetInputSlot(0));
2135
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002136 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002137}
2138
2139template<typename HalPolicy,
2140 typename HalOperation = typename HalPolicy::Operation,
2141 typename HalModel = typename HalPolicy::Model>
2142bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2143{
2144 using HalOperand = typename HalPolicy::Operand;
2145 using HalOperandType = typename HalPolicy::OperandType;
2146
2147 ALOGV("HalPolicy::ConvertSoftmax()");
2148
2149 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2150 if (!input.IsValid())
2151 {
2152 return Fail("%s: Operation has invalid inputs", __func__);
2153 }
2154
2155 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2156 if (!outputOperand)
2157 {
2158 return Fail("%s: Operation has no outputs", __func__);
2159 }
2160
2161 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002162
2163 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002164 HalOperandType outputType = outputOperand->type;
2165
2166 // Read beta value
2167 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002168 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002169 Half value;
2170
2171 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2172 {
2173 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2174 }
2175
2176 desc.m_Beta = static_cast<float>(value);
2177 }
2178 else
2179 {
2180 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2181 {
2182 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2183 }
Kevin May42477c12020-03-26 13:34:14 +00002184 }
2185
2186 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002187 2,
2188 HalOperandType::INT32,
2189 desc.m_Axis,
2190 model,
2191 data))
Kevin May42477c12020-03-26 13:34:14 +00002192 {
2193 return Fail("%s: Operation has invalid inputs", __func__);
2194 }
2195
Kevin May42477c12020-03-26 13:34:14 +00002196 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002197 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2198 {
2199 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2200 IsSoftmaxSupported,
2201 data.m_Backends,
2202 isSupported,
2203 input.GetTensorInfo(),
2204 outputInfo,
2205 desc);
2206 };
2207
2208 if(IsDynamicTensor(outputInfo))
2209 {
2210 isSupported = AreDynamicTensorsSupported();
2211 }
2212 else
2213 {
2214 validateFunc(outputInfo, isSupported);
2215 }
2216
Kevin May42477c12020-03-26 13:34:14 +00002217 if (!isSupported)
2218 {
2219 return false;
2220 }
2221
2222 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
2223 assert(layer != nullptr);
2224 input.Connect(layer->GetInputSlot(0));
2225
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002226 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002227}
2228
2229template<typename HalPolicy,
2230 typename HalOperation = typename HalPolicy::Operation,
2231 typename HalModel = typename HalPolicy::Model>
2232bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2233{
2234 using HalOperand = typename HalPolicy::Operand;
2235 using HalOperandType = typename HalPolicy::OperandType;
2236
2237 ALOGV("HalPolicy::ConvertLstm()");
2238
2239 // Inputs:
2240 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2241 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2242 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2243 if (!input.IsValid())
2244 {
2245 return Fail("%s: Could not read input 0: input", __func__);
2246 }
2247 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2248 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2249 if (!outputStateIn.IsValid())
2250 {
2251 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2252 }
2253 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2254 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2255 if (!cellStateIn.IsValid())
2256 {
2257 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2258 }
2259
2260 // Get the mandatory input tensors:
2261 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2262 // [num_units, input_size].
2263 const ConstTensorPin inputToForgetWeightsPin =
2264 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2265 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2266 // [num_units, input_size].
2267 const ConstTensorPin inputToCellWeightsPin =
2268 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2269 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2270 // [num_units, input_size].
2271 const ConstTensorPin inputToOutputWeightsPin =
2272 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2273 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2274 // [num_units, output_size].
2275 const ConstTensorPin recurrentToForgetWeightsPin =
2276 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2277 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2278 // [num_units, output_size].
2279 const ConstTensorPin recurrentToCellWeightsPin =
2280 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2281 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2282 // [num_units, output_size].
2283 const ConstTensorPin recurrentToOutputWeightsPin =
2284 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2285 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2286 const ConstTensorPin forgetGateBiasPin =
2287 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2288 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2289 const ConstTensorPin cellBiasPin =
2290 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2291 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2292 const ConstTensorPin outputGateBiasPin =
2293 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2294
2295 if (!inputToForgetWeightsPin.IsValid() ||
2296 !inputToCellWeightsPin.IsValid() ||
2297 !inputToOutputWeightsPin.IsValid() ||
2298 !recurrentToForgetWeightsPin.IsValid() ||
2299 !recurrentToCellWeightsPin.IsValid() ||
2300 !recurrentToOutputWeightsPin.IsValid() ||
2301 !forgetGateBiasPin.IsValid() ||
2302 !cellBiasPin.IsValid() ||
2303 !outputGateBiasPin.IsValid())
2304 {
2305 return Fail("%s: Operation has invalid tensor inputs", __func__);
2306 }
2307
2308 // Get the optional input tensors:
2309 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2310 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2311 const ConstTensorPin inputToInputWeightsPin =
2312 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2313 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2314 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2315 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2316 const ConstTensorPin recurrentToInputWeightsPin =
2317 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2318 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2319 const ConstTensorPin cellToInputWeightsPin =
2320 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2321 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2322 const ConstTensorPin cellToForgetWeightsPin =
2323 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2324 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2325 const ConstTensorPin cellToOutputWeightsPin =
2326 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2327 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2328 const ConstTensorPin inputGateBiasPin =
2329 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2330 12,
2331 model,
2332 data,
2333 g_DontPermute,
2334 nullptr,
2335 true);
2336
2337 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2338 // [output_size, num_units].
2339 const ConstTensorPin projectionWeightsPin =
2340 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2341 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2342 const ConstTensorPin projectionBiasPin =
2343 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2344 17,
2345 model,
2346 data,
2347 g_DontPermute,
2348 nullptr,
2349 true);
2350
2351 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2352 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2353 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2354 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2355 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2356 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2357 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2358 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2359 {
2360 return Fail("%s: Operation has invalid tensor inputs", __func__);
2361 }
2362
2363 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2364 // 20: The activation function: A value indicating the activation function:
2365 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2366 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2367 // If set to 0.0 then clipping is disabled.
2368 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2369 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2370 ActivationFn activation;
2371 float cellClip;
2372 float projClip;
2373 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2374 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2375 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2376 {
2377 return Fail("%s: Operation has invalid scalar inputs", __func__);
2378 }
2379
2380 // Get the normalization tensors
2381 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2382 // Used to rescale normalized inputs to activation at input gate.
2383 const ConstTensorPin inputLayerNormWeightsPin
2384 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2385
2386 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2387 // Used to rescale normalized inputs to activation at forget gate.
2388 const ConstTensorPin forgetLayerNormWeightsPin =
2389 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2390 24,
2391 model,
2392 data,
2393 g_DontPermute,
2394 nullptr,
2395 true);
2396
2397 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2398 // Used to rescale normalized inputs to activation at cell gate.
2399 const ConstTensorPin cellLayerNormWeightsPin =
2400 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2401 25,
2402 model,
2403 data,
2404 g_DontPermute,
2405 nullptr,
2406 true);
2407
2408 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2409 // Used to rescale normalized inputs to activation at output gate.
2410 const ConstTensorPin outputLayerNormWeightsPin =
2411 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2412 26,
2413 model,
2414 data,
2415 g_DontPermute,
2416 nullptr,
2417 true);
2418
2419 // Outputs:
2420 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2421 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2422 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2423 if (!scratchBuffer)
2424 {
2425 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2426 }
2427 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2428 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2429 if (!outputStateOut)
2430 {
2431 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2432 }
2433 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2434 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2435 if (!cellStateOut)
2436 {
2437 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2438 }
2439 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2440 // effectively the same as the current “output state (out)” value.
2441 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2442 if (!output)
2443 {
2444 return Fail("%s: Could not read output 3: output", __func__);
2445 }
2446
2447 // set the params structure for the AddLstmLayer call
2448 LstmInputParams params;
2449 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2450 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2451 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2452 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2453 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2454 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2455 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2456 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2457 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2458 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2459 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2460 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2461 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2462 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2463 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2464 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2465 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2466 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2467 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2468 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2469 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2470
2471 // set the layer descriptor
2472 LstmDescriptor desc;
2473 desc.m_ActivationFunc = activation;
2474 desc.m_ClippingThresCell = cellClip;
2475 desc.m_ClippingThresProj = projClip;
2476 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2477 params.m_RecurrentToInputWeights == nullptr ||
2478 params.m_InputGateBias == nullptr);
2479 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2480 params.m_CellToOutputWeights != nullptr);
2481 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2482 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2483 params.m_ForgetLayerNormWeights != nullptr ||
2484 params.m_CellLayerNormWeights != nullptr ||
2485 params.m_OutputLayerNormWeights != nullptr);
2486
2487 // validate the optional input groups
2488 if (desc.m_CifgEnabled &&
2489 (params.m_InputToInputWeights != nullptr ||
2490 params.m_RecurrentToInputWeights != nullptr ||
2491 params.m_InputGateBias != nullptr))
2492 {
2493 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2494 " and input gate bias must be provided", __func__);
2495 }
2496
2497 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2498 {
2499 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2500 }
2501
2502 if (desc.m_PeepholeEnabled &&
2503 (params.m_CellToForgetWeights == nullptr ||
2504 params.m_CellToOutputWeights == nullptr ||
2505 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2506 {
2507 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2508 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2509 }
2510
2511 if (desc.m_LayerNormEnabled &&
2512 (params.m_ForgetLayerNormWeights == nullptr ||
2513 params.m_CellLayerNormWeights == nullptr ||
2514 params.m_OutputLayerNormWeights == nullptr ||
2515 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2516 {
2517 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2518 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2519 }
2520
2521 // Check if the layer is supported
2522 // Inputs
2523 const TensorInfo& inputInfo = input.GetTensorInfo();
2524 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2525 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2526
2527 // Outputs
2528 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2529 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2530 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2531 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2532
Kevin May42477c12020-03-26 13:34:14 +00002533 // Basic parameters
2534 LstmInputParamsInfo paramsInfo;
2535 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2536 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2537 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2538 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2539 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2540 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2541 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2542 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2543 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2544
2545 // Optional parameters
2546 if (!desc.m_CifgEnabled)
2547 {
2548 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2549 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2550 if (params.m_CellToInputWeights != nullptr)
2551 {
2552 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2553 }
2554 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2555 }
2556
2557 if (desc.m_ProjectionEnabled)
2558 {
2559 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2560 if (params.m_ProjectionBias != nullptr)
2561 {
2562 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2563 }
2564 }
2565
2566 if (desc.m_PeepholeEnabled)
2567 {
2568 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2569 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2570 }
2571
2572 if (desc.m_LayerNormEnabled)
2573 {
2574 if(!desc.m_CifgEnabled)
2575 {
2576 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2577 }
2578 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2579 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2580 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2581 }
2582
2583 bool isSupported = false;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002584 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2585 {
2586 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2587 IsLstmSupported,
2588 data.m_Backends,
2589 isSupported,
2590 inputInfo,
2591 outputStateInInfo,
2592 cellStateInInfo,
2593 scratchBufferInfo,
2594 outputStateOutInfo,
2595 cellStateOutInfo,
2596 outputInfo,
2597 desc,
2598 paramsInfo);
2599 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002600
Sadik Armagandbda4b72020-09-03 11:33:07 +01002601 bool isDynamic = false;
2602 if (!IsDynamicTensor(outputStateOutInfo) &&
2603 !IsDynamicTensor(scratchBufferInfo) &&
2604 !IsDynamicTensor(cellStateOutInfo) &&
2605 !IsDynamicTensor(outputInfo))
2606 {
2607 validateFunc(outputInfo, isSupported);
2608 }
2609 else
2610 {
2611 isDynamic = true;
2612 isSupported = AreDynamicTensorsSupported();
2613 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002614
Kevin May42477c12020-03-26 13:34:14 +00002615 if (!isSupported)
2616 {
2617 return false;
2618 }
2619
2620 // Add the layer
2621 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2622
2623 input.Connect(layer->GetInputSlot(0));
2624 outputStateIn.Connect(layer->GetInputSlot(1));
2625 cellStateIn.Connect(layer->GetInputSlot(2));
2626
Sadik Armagandbda4b72020-09-03 11:33:07 +01002627 if (!isDynamic)
2628 {
2629 return (
2630 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2631 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2632 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2633 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2634 }
2635 else
2636 {
2637 return (
2638 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2639 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2640 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2641 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002642 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002643 }
2644
Kevin May42477c12020-03-26 13:34:14 +00002645}
2646
2647template<typename HalPolicy,
2648 typename HalOperation = typename HalPolicy::Operation,
2649 typename HalModel = typename HalPolicy::Model>
2650bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2651{
2652 using HalOperand = typename HalPolicy::Operand;
2653 using HalOperandType = typename HalPolicy::OperandType;
2654
2655 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2656
2657 if (!input.IsValid())
2658 {
2659 return Fail("%s: Operation has invalid inputs", __func__);
2660 }
2661
2662 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2663
2664 if (!output)
2665 {
2666 return Fail("%s: Could not read output 0", __func__);
2667 }
2668
2669 const TensorInfo& inputInfo = input.GetTensorInfo();
2670 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002671
2672 // ArmNN does not currently support non-fixed weights or bias
2673 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2674 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2675
2676 if (weightsOperand == nullptr)
2677 {
2678 return Fail("%s: Operand is invalid", __func__);
2679 }
2680 TransposeConvolution2dDescriptor desc;
2681 desc.m_DataLayout = DataLayout::NHWC;
2682
2683 // Determine whether padding is implicit or explicit
2684 bool implicitPadding = operation.inputs.size() == 9;
2685
2686 if (implicitPadding )
2687 {
2688 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2689 }
2690 else
2691 {
2692 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2693 }
2694
2695 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2696 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2697 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2698
2699 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2700
2701 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2702 // We have to permute it to OIHW if the data layout is NCHW.
2703 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2704 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2705 model, data, OHWIToOIHW) :
2706 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2707
2708 // Bias is a 1D tensor
2709 const ConstTensorPin biasPin =
2710 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2711
2712 if (!weightsPin.IsValid())
2713 {
2714 return Fail("%s: Operation has invalid weights", __func__);
2715 }
2716
2717 if (!biasPin.IsValid())
2718 {
2719 return Fail("%s: Operation has invalid biases", __func__);
2720 }
2721
2722 ConstTensor weights = weightsPin.GetConstTensor();
2723 ConstTensor bias = biasPin.GetConstTensor();
2724 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2725
2726 ActivationFn activation;
2727
2728 if (implicitPadding)
2729 {
2730 int32_t strideX{0};
2731 int32_t strideY{0};
2732 int32_t padLeft{0};
2733 int32_t padRight{0};
2734 int32_t padTop{0};
2735 int32_t padBottom{0};
2736
2737 android::nn::PaddingScheme paddingScheme;
2738 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2739 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2740 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2741 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2742 {
2743 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2744 }
2745
2746 const uint32_t kernelX = weights.GetShape()[widthIndex];
2747 const uint32_t kernelY = weights.GetShape()[heightIndex];
2748 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2749 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
2750
2751 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2752 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
2753
2754 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2755 // but Arm NN only supports values >= 0
2756 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2757 {
2758 return Fail("%s: Negative padding values are not supported", __func__);
2759 }
2760
Colm Donelan8f3d33e2020-07-06 15:41:43 +01002761 // If output shape has been specified as a parameter then extract it and make it available.
2762 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
2763 std::vector<int32_t> outputShape;
2764 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
2765 {
2766 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
2767 for (int dimension : outputShape)
2768 {
2769 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
2770 }
2771 desc.m_OutputShapeEnabled = true;
2772 }
2773
Matthew Sloyan9b088d92020-09-14 15:12:55 +01002774 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
2775 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
2776 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
2777 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
2778 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
2779 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00002780 }
2781 else if (operation.inputs.size() == 11)
2782 {
2783 // explicit padding
2784 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2785 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2786 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2787 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2788 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2789 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
2790 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
2791 {
2792 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2793 }
2794 }
2795 else
2796 {
2797 return Fail("%s: Unsupported number of operation inputs", __func__);
2798 }
2799
2800 desc.m_BiasEnabled = true;
2801 Optional<TensorInfo> biases(bias.GetInfo());
2802
2803 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002804 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2805 {
2806 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2807 IsTransposeConvolution2dSupported,
2808 data.m_Backends,
2809 isSupported,
2810 inputInfo,
2811 outputInfo,
2812 desc,
2813 weights.GetInfo(),
2814 biases);
2815 };
2816
2817 if(IsDynamicTensor(outputInfo))
2818 {
2819 isSupported = AreDynamicTensorsSupported();
2820 }
2821 else
2822 {
2823 validateFunc(outputInfo, isSupported);
2824 }
Kevin May42477c12020-03-26 13:34:14 +00002825 if (!isSupported)
2826 {
2827 return false;
2828 }
2829
2830 IConnectableLayer* startLayer =
2831 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
2832 if (!startLayer)
2833 {
2834 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2835 }
2836
Kevin May42477c12020-03-26 13:34:14 +00002837 input.Connect(startLayer->GetInputSlot(0));
2838
Kevin Mayfcf2a152020-09-08 16:06:32 +01002839 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
2840 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00002841}
2842
2843} // armnn_driver namespace