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