blob: 760312e7afb5d021c3c2783821125b1b4658db62 [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
344 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
345
346 if (!endLayer)
347 {
348 return Fail("%s: ProcessActivation failed", __func__);
349 }
350
351 input.Connect(startLayer->GetInputSlot(0));
352
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100353 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000354}
355
356template<typename HalPolicy,
357 typename HalOperation = typename HalPolicy::Operation,
358 typename HalModel = typename HalPolicy::Model>
359bool ConvertDepthwiseConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
360{
361 using HalOperand = typename HalPolicy::Operand;
362 using HalOperandType = typename HalPolicy::OperandType;
363
364 ALOGV("HalPolicy::ConvertDepthwiseConv2d_1_2()");
365
366 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
367
368 if (!input.IsValid())
369 {
370 return Fail("%s: Operation has invalid inputs", __func__);
371 }
372
373 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
374
375 if (!output)
376 {
377 return Fail("%s: Could not read output 0", __func__);
378 }
379
380 const TensorInfo& inputInfo = input.GetTensorInfo();
381 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
382
Kevin May42477c12020-03-26 13:34:14 +0000383 // ArmNN does not currently support non-fixed weights or bias
384 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
385 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
386
387 if (weightsOperand == nullptr)
388 {
389 return Fail("%s: Operand is invalid", __func__);
390 }
391 if ( weightsOperand->dimensions[0] != 1)
392 {
393 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
394 __func__, weightsOperand->dimensions[0] );
395 }
396
397 DepthwiseConvolution2dDescriptor desc;
398 desc.m_DataLayout = DataLayout::NHWC;
399
400 // Determine whether padding is implicit or explicit
401 bool implicitPadding = operation.inputs.size() == 8 ||
402 (operation.inputs.size() >= 9 &&
403 GetInputOperand<HalPolicy>(operation, 8, model)->type == HalOperandType::BOOL);
404
405 // Look ahead to find the optional DataLayout, if present
406 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
407 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, dataLayoutFlagIndex, model, data);
408
409 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
410 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
411 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
412 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
413
414 // Reinterpret weight data as [ H, W, I, M ]
415 TensorShape weightsShape({ weightsOperand->dimensions[1],
416 weightsOperand->dimensions[2],
417 inputInfo.GetShape()[channelsIndex],
418 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
419
420 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
421 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
422
423 const ConstTensorPin weightsPin =
424 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
425 1,
426 model,
427 data,
428 HWIMToMIHW,
429 &weightsShape);
430
431 // Bias is a 1D tensor
432 const ConstTensorPin biasPin =
433 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
434
435 if (!weightsPin.IsValid())
436 {
437 return Fail("%s: Operation has invalid weights", __func__);
438 }
439
440 if (!biasPin.IsValid())
441 {
442 return Fail("%s: Operation has invalid biases", __func__);
443 }
444
445 ConstTensor weights = weightsPin.GetConstTensor();
446 ConstTensor bias = biasPin.GetConstTensor();
447 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
448
449 ActivationFn activation;
450
451 if (implicitPadding)
452 {
453 android::nn::PaddingScheme paddingScheme;
454 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
455 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
456 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
457 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data) ||
458 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 9, desc, model, data))
459 {
460 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
461 }
462
463 const uint32_t kernelX = weights.GetShape()[3];
464 const uint32_t kernelY = weights.GetShape()[2];
465 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
466 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
467
468 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
469 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
470 }
471 else if (operation.inputs.size() >= 11)
472 {
473 // explicit padding
474 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
475 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
476 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
477 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
478 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
479 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
480 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data) ||
481 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 12, desc, model, data))
482 {
483 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
484 }
485 }
486 else
487 {
488 return Fail("%s: Unsupported number of operation inputs", __func__);
489 }
490
491 desc.m_BiasEnabled = true;
492 Optional<TensorInfo> biases(bias.GetInfo());
493
494 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100495 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
496 {
497 FORWARD_LAYER_SUPPORT_FUNC(__func__,
498 IsDepthwiseConvolutionSupported,
499 data.m_Backends,
500 isSupported,
501 inputInfo,
502 outputInfo,
503 desc,
504 weights.GetInfo(),
505 biases);
506 };
507
508 if(!IsDynamicTensor(outputInfo))
509 {
510 validateFunc(outputInfo, isSupported);
511 }
512 else
513 {
514 isSupported = AreDynamicTensorsSupported();
515 }
Kevin May42477c12020-03-26 13:34:14 +0000516
517 if (!isSupported)
518 {
519 return false;
520 }
521
522 IConnectableLayer* startLayer =
523 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
524
525 if (!startLayer)
526 {
527 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
528 }
529
530 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
531 if (!endLayer)
532 {
533 return Fail("%s: ProcessActivation failed", __func__);
534 }
535
536 input.Connect(startLayer->GetInputSlot(0));
537
538 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
539}
540
541template<typename HalPolicy,
542 typename HalOperation = typename HalPolicy::Operation,
543 typename HalModel = typename HalPolicy::Model>
544bool ConvertDequantize_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
545{
546 ALOGV("HalPolicy::ConvertDequantize()");
547
548 if (IsQSymmDequantizeForWeights<HalPolicy>(operation, model))
549 {
550 // NOTE: QSymm8 weights are dequantized internally by the driver,
551 // therefore this type of Dequantize is implicitly supported
552 return true;
553 }
554
555 return ::ConvertDequantize<HalPolicy>(operation, model, data);
556}
557
558template<typename HalPolicy,
559 typename HalOperation = typename HalPolicy::Operation,
560 typename HalModel = typename HalPolicy::Model>
561bool ConvertElementwiseUnary(const HalOperation& operation,
562 const HalModel& model,
563 ConversionData& data,
564 UnaryOperation unaryOperation)
565{
566 using HalOperand = typename HalPolicy::Operand;
567
568 ALOGV("HalPolicy::ConvertElementwiseUnary()");
569 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
570
571 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
572
573 if (!input.IsValid())
574 {
575 return Fail("%s: Operation has invalid input", __func__);
576 }
577
578 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
579 if (!output)
580 {
581 return Fail("%s: Could not read output 0", __func__);
582 }
583
584 const TensorInfo& inputInfo = input.GetTensorInfo();
585 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
586
Kevin May42477c12020-03-26 13:34:14 +0000587 ElementwiseUnaryDescriptor descriptor(unaryOperation);
588
589 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100590
591 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
592 {
593 FORWARD_LAYER_SUPPORT_FUNC(__func__,
594 IsElementwiseUnarySupported,
595 data.m_Backends,
596 isSupported,
597 inputInfo,
598 outputInfo,
599 descriptor);
600 };
601
602 if(!IsDynamicTensor(outputInfo))
603 {
604 validateFunc(outputInfo, isSupported);
605 }
606 else
607 {
608 isSupported = AreDynamicTensorsSupported();
609 }
Kevin May42477c12020-03-26 13:34:14 +0000610
611 if (!isSupported)
612 {
613 return false;
614 }
615
616 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
617 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +0000618 input.Connect(layer->GetInputSlot(0));
619
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100620 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000621}
622
623template<typename HalPolicy,
624 typename HalOperation = typename HalPolicy::Operation,
625 typename HalModel = typename HalPolicy::Model>
626bool ConvertExpandDims(const HalOperation& operation, const HalModel& model, ConversionData& data)
627{
628 using HalOperand = typename HalPolicy::Operand;
629 using HalOperandType = typename HalPolicy::OperandType;
630
631 ALOGV("HalPolicy::ConvertExpandDims()");
632
633 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
634
635 if (!input.IsValid())
636 {
637 return Fail("%s: Operation has invalid input", __func__);
638 }
639
640 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
641 if (!output)
642 {
643 return Fail("%s: Operation has invalid output", __func__);
644 }
645
646 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000647
648 int32_t axis;
649 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
650 {
651 return Fail("%s: failed to get axis input value", __func__);
652 }
653
654 TensorShape targetShape;
655
656 try
657 {
658 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
659 }
660 catch (const std::exception& e)
661 {
662 return Fail("%s: %s", __func__, e.what());
663 }
664
665 if (targetShape != outputInfo.GetShape())
666 {
667 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
668 }
669
670 ReshapeDescriptor reshapeDescriptor;
671 reshapeDescriptor.m_TargetShape = targetShape;
672
673 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100674 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
675 {
676 FORWARD_LAYER_SUPPORT_FUNC(__func__,
677 IsReshapeSupported,
678 data.m_Backends,
679 isSupported,
680 input.GetTensorInfo(),
681 outputInfo,
682 reshapeDescriptor);
683 };
684
685 if(!IsDynamicTensor(outputInfo))
686 {
687 validateFunc(outputInfo, isSupported);
688 }
689 else
690 {
691 isSupported = AreDynamicTensorsSupported();
692 }
Kevin May42477c12020-03-26 13:34:14 +0000693
694 if (!isSupported)
695 {
696 return false;
697 }
698
699 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
700 assert(layer != nullptr);
701 input.Connect(layer->GetInputSlot(0));
702
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100703 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000704}
705
706template<typename HalPolicy,
Teresa Charlinf931af92020-04-10 16:46:53 +0100707 typename HalOperation = typename HalPolicy::Operation,
708 typename HalModel = typename HalPolicy::Model>
709bool ConvertGather(const HalOperation& operation, const HalModel& model, ConversionData& data)
710{
711 using HalOperand = typename HalPolicy::Operand;
712 using HalOperandType = typename HalPolicy::OperandType;
713
714 ALOGV("HalPolicy::ConvertGather()");
715
716 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
717 if (!input.IsValid())
718 {
719 return Fail("%s: Operation has invalid input", __func__);
720 }
721 auto inputDimensions = input.GetTensorInfo().GetNumDimensions();
722
723 LayerInputHandle indices = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data);
724 if (!indices.IsValid())
725 {
726 return Fail("%s: Operation has invalid indices", __func__);
727 }
728 auto indicesDimensions = indices.GetTensorInfo().GetNumDimensions();
729
730 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
731 if (!output)
732 {
733 return Fail("%s: Operation has invalid output", __func__);
734 }
735 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
736 auto outputDimensions = outputInfo.GetNumDimensions();
Teresa Charlinf931af92020-04-10 16:46:53 +0100737 if (outputDimensions != inputDimensions + indicesDimensions - 1)
738 {
739 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 +0100740 __func__, outputDimensions, inputDimensions, indicesDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100741 }
742
743 int32_t axis;
744 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
745 {
746 return Fail("%s: Operation has invalid or unsupported axis operand", __func__);
747 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100748 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
Teresa Charlinf931af92020-04-10 16:46:53 +0100749 {
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100750 return Fail("%s: Operation has invalid axis: %d. It is out of bounds [-%d, %d))", __func__, axis,
751 inputDimensions, inputDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100752 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100753
754 GatherDescriptor desc;
755 desc.m_Axis = axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100756
757 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100758 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
759 {
760 FORWARD_LAYER_SUPPORT_FUNC(__func__,
761 IsGatherSupported,
762 data.m_Backends,
763 isSupported,
764 input.GetTensorInfo(),
765 indices.GetTensorInfo(),
766 outputInfo,
767 desc);
768 };
769
770 if(!IsDynamicTensor(outputInfo))
771 {
772 validateFunc(outputInfo, isSupported);
773 }
774 else
775 {
776 isSupported = AreDynamicTensorsSupported();
777 }
778
Teresa Charlinf931af92020-04-10 16:46:53 +0100779 if (!isSupported)
780 {
781 return false;
782 }
783
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100784 IConnectableLayer* layer = data.m_Network->AddGatherLayer(desc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100785 assert(layer != nullptr);
786 input.Connect(layer->GetInputSlot(0));
787 indices.Connect(layer->GetInputSlot(1));
788
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100789 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100790}
791
792template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000793 typename HalOperation = typename HalPolicy::Operation,
794 typename HalModel = typename HalPolicy::Model>
795bool ConvertGroupedConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
796{
797 using HalOperand = typename HalPolicy::Operand;
798 using HalOperandType = typename HalPolicy::OperandType;
799
800 ALOGV("HalPolicy::ConvertGroupedConv2d()");
801
802 //
803 // Parse data
804 //
805 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
806 if (!input.IsValid())
807 {
808 return Fail("%s: Operation has invalid inputs", __func__);
809 }
810 const TensorInfo& inputInfo = input.GetTensorInfo();
811
812 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
813 if (!output)
814 {
815 return Fail("%s: Could not read output 0", __func__);
816 }
817 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000818
819 // Look ahead to determine data layout
820 DataLayout dataLayout = DataLayout::NHWC;
821 if (operation.inputs.size() == 12)
822 {
823 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
824 }
825 else
826 {
827 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
828 }
829
830 // NOTE:
831 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
832 // but Arm NN expects the filter's height and width indices to match the input's height and
833 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
834 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
835 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
836 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
837 model, data, ohwiToOihw) :
838 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
839 const ConstTensorPin biasesPin =
840 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
841 if (!weightsPin.IsValid() || !biasesPin.IsValid())
842 {
843 return Fail("%s: Operation has invalid inputs", __func__);
844 }
845
846 ConstTensor weights = weightsPin.GetConstTensor();
847 ConstTensor biases = biasesPin.GetConstTensor();
848 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
849
850 const TensorShape& inputShape = inputInfo.GetShape();
851 const TensorShape& outputShape = outputInfo.GetShape();
852 const TensorShape& weightsShape = weights.GetShape();
853 const TensorShape& biasesShape = biases.GetShape();
854
855 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
856 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
857 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
858 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
859
860 Convolution2dDescriptor desc;
861 desc.m_DataLayout = dataLayout;
862 desc.m_BiasEnabled = true;
863
864 int numGroups;
865 ActivationFn activation;
866
867 if (operation.inputs.size() == 12)
868 {
869 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
870 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
871 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
872 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
873 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
874 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
875 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
876 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
877 {
878 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
879 }
880
881 }
882 else if (operation.inputs.size() == 9)
883 {
884 android::nn::PaddingScheme paddingScheme;
885 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
886 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
887 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
888 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
889 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
890 {
891 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
892 }
893
894 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
895 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
896
897 const uint32_t kernelX = weightsShape[widthIndex];
898 const uint32_t kernelY = weightsShape[heightIndex];
899
900 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
901 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
902 }
903 else
904 {
905 return Fail("%s: Unsupported number of operation inputs", __func__);
906 }
907
908 const unsigned int outputChannels = outputShape[channelsIndex];
909
910 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
911 const unsigned int channelMultiplier = outputChannels / numGroups;
912
913 //
914 // Validate all relevant inputs
915 //
916 if (numGroups <= 0)
917 {
918 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
919 }
920
921 if (outputChannels % numGroups != 0u)
922 {
923 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
924 }
925
926 //
927 // Set up Splitter layer
928 //
929 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
930 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
931
932 TensorInfo splitterOutputInfo(4,
933 splitterDimSizes,
934 inputInfo.GetDataType(),
935 inputInfo.GetQuantizationScale(),
936 inputInfo.GetQuantizationOffset());
937
938 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
939
940 ViewsDescriptor splitterDesc(numGroups);
941 for (unsigned int group = 0u; group < numGroups; ++group)
942 {
943 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
944 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
945 {
946 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
947 }
948 }
949
950 bool isSupported = false;
951 FORWARD_LAYER_SUPPORT_FUNC(__func__,
952 IsSplitterSupported,
953 data.m_Backends,
954 isSupported,
955 inputInfo,
956 splitterOutputInfos,
957 splitterDesc);
958 if (!isSupported)
959 {
960 return false;
961 }
962
963 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
964 if (!splitterLayer)
965 {
966 return Fail("%s: Failed to add SplitterLayer", __func__);
967 }
968
969 input.Connect(splitterLayer->GetInputSlot(0));
970 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
971 {
972 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
973 }
974
975 //
976 // Set up Convolution2d layers for each group
977 //
978
979 // Set up group tensor shapes
980 TensorShape groupInputShape(inputShape);
981 groupInputShape[channelsIndex] = channelsPerGroup;
982
983 TensorShape groupOutputShape(outputShape);
984 groupOutputShape[channelsIndex] = 1;
985
986 TensorShape groupWeightsShape(weightsShape);
987 groupWeightsShape[0] /= channelMultiplier * numGroups;
988
989 TensorShape groupBiasesShape({ 1 });
990
991 // Set up group tensor infos
992 TensorInfo groupInputInfo(inputInfo);
993 groupInputInfo.SetShape(groupInputShape);
994
995 const TensorInfo& weightsInfo = weights.GetInfo();
996 TensorInfo groupWeightsInfo(weightsInfo);
997 groupWeightsInfo.SetShape(groupWeightsShape);
998
999 const TensorInfo& biasesInfo = biases.GetInfo();
1000 TensorInfo groupBiasesInfo(biasesInfo);
1001 groupBiasesInfo.SetShape(groupBiasesShape);
1002
1003 TensorInfo groupOutputInfo(outputInfo);
1004 groupOutputInfo.SetShape(groupOutputShape);
1005
1006 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1007 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1008
1009 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1010 for (unsigned int group = 0u; group < numGroups; ++group)
1011 {
1012 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1013 {
1014 auto index = group * channelMultiplier + m;
1015
1016 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1017 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1018
1019 if (weightsInfo.HasPerAxisQuantization())
1020 {
1021 // Extract per-axis quantization scales for group weights
1022 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1023 groupWeightsInfo.SetQuantizationScales(
1024 std::vector<float>(weightsQuantScales.begin() + index,
1025 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1026
1027 // Extract per-axis quantization scales for group biases
1028 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1029 groupBiasesInfo.SetQuantizationScales(
1030 std::vector<float>(biasesQuantScales.begin() + index,
1031 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1032 }
1033
1034 // Extract weights and biases data for current group convolution
1035 ConstTensor groupWeights(groupWeightsInfo,
1036 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1037 weightsDataOffset));
1038 ConstTensor groupBiases(groupBiasesInfo,
1039 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1040 biasesDataOffset));
1041
1042 isSupported = false;
1043 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1044 IsConvolution2dSupported,
1045 data.m_Backends,
1046 isSupported,
1047 groupInputInfo,
1048 groupOutputInfo,
1049 desc,
1050 groupWeightsInfo,
1051 Optional<TensorInfo>(groupBiasesInfo));
1052 if (!isSupported)
1053 {
1054 return false;
1055 }
1056
1057 IConnectableLayer* convLayer =
1058 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1059 if (!convLayer)
1060 {
1061 return Fail("%s: AddConvolution2dLayer failed", __func__);
1062 }
1063
1064 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1065 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1066
1067 convLayers[index] = convLayer;
1068 }
1069 }
1070
1071 //
1072 // Set up Concat layer
1073 //
1074 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
1075 for (unsigned int group = 0u; group < numGroups; ++group)
1076 {
1077 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1078 {
1079 auto index = group * channelMultiplier + m;
1080 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1081 concatDescriptor.SetConcatAxis(channelsIndex);
1082 }
1083 }
1084
1085 isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001086 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1087 {
1088 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1089 IsConcatSupported,
1090 data.m_Backends,
1091 isSupported,
1092 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1093 outputInfo,
1094 concatDescriptor);
1095 };
1096
1097 if(!IsDynamicTensor(outputInfo))
1098 {
1099 validateFunc(outputInfo, isSupported);
1100 }
1101 else
1102 {
1103 isSupported = AreDynamicTensorsSupported();
1104 }
1105
Kevin May42477c12020-03-26 13:34:14 +00001106 if (!isSupported)
1107 {
1108 return false;
1109 }
1110
1111 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1112 if (!concatLayer)
1113 {
1114 return Fail("%s: AddConcatLayer failed", __func__);
1115 }
1116
1117 for (unsigned int group = 0u; group < numGroups; ++group)
1118 {
1119 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1120 {
1121 auto index = group * channelMultiplier + m;
1122 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1123 }
1124 }
1125 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1126
1127 //
1128 // Set up Activation layer (if it is set)
1129 //
1130 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
1131 if (!endLayer)
1132 {
1133 return Fail("%s: ProcessActivation failed", __func__);
1134 }
1135
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001136 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001137}
1138
1139template<typename HalPolicy,
1140 typename HalOperation = typename HalPolicy::Operation,
1141 typename HalModel = typename HalPolicy::Model>
1142bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1143{
1144 using HalOperand = typename HalPolicy::Operand;
1145 using HalOperandType = typename HalPolicy::OperandType;
1146
1147 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1148
1149 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1150 if (!input.IsValid())
1151 {
1152 return Fail("%s: Operation has an invalid input 0", __func__);
1153 }
1154
1155 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1156 if (!output)
1157 {
1158 return Fail("%s: Operation has an invalid output", __func__);
1159 }
1160
1161 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001162
1163 // Determine data type of input tensor
1164 HalOperandType inputType;
1165 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1166 {
1167 return Fail("%s: Operation has invalid inputs", __func__);
1168 }
1169
1170 InstanceNormalizationDescriptor desc;
1171
1172 // Read gamma, beta & epsilon
1173 if (inputType == HalOperandType::TENSOR_FLOAT16)
1174 {
1175 Half fp16Gamma;
1176 Half fp16Beta;
1177 Half fp16Epsilon;
1178
1179 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1180 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1181 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1182 {
1183 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1184 }
1185
1186 desc.m_Gamma = static_cast<float>(fp16Gamma);
1187 desc.m_Beta = static_cast<float>(fp16Beta);
1188 desc.m_Eps = static_cast<float>(fp16Epsilon);
1189 }
1190 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1191 {
1192 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1193 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1194 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1195 {
1196 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1197 }
1198 }
1199 else
1200 {
1201 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1202 }
1203
1204 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1205
1206 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001207 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1208 {
1209 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1210 IsInstanceNormalizationSupported,
1211 data.m_Backends,
1212 isSupported,
1213 input.GetTensorInfo(),
1214 outputInfo,
1215 desc);
1216 };
1217
1218 if(IsDynamicTensor(outputInfo))
1219 {
1220 isSupported = AreDynamicTensorsSupported();
1221 }
1222 else
1223 {
1224 validateFunc(outputInfo, isSupported);
1225 }
1226
Kevin May42477c12020-03-26 13:34:14 +00001227 if (!isSupported)
1228 {
1229 return false;
1230 }
1231
1232 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1233 input.Connect(layer->GetInputSlot(0));
1234
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001235 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001236}
1237
1238template<typename HalPolicy,
1239 typename HalOperation = typename HalPolicy::Operation,
1240 typename HalModel = typename HalPolicy::Model>
1241bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1242{
1243 using HalOperand = typename HalPolicy::Operand;
1244 using HalOperandType = typename HalPolicy::OperandType;
1245
1246 ALOGV("HalPolicy::ConvertLogSoftmax()");
1247
1248 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1249 if (!input.IsValid())
1250 {
1251 return Fail("%s: Failed to read input 0", __func__);
1252 }
1253
1254 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1255 if (!output)
1256 {
1257 return Fail("%s: Failed to read output", __func__);
1258 }
1259
1260 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001261
1262 // Determine data type of input tensor
1263 HalOperandType inputType;
1264 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1265 {
1266 return Fail("%s: Operation has invalid inputs", __func__);
1267 }
1268
1269 LogSoftmaxDescriptor descriptor;
1270
1271 // Read beta
1272 if (inputType == HalOperandType::TENSOR_FLOAT16)
1273 {
1274 Half fp16Beta;
1275 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1276 {
1277 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1278 }
1279
1280 descriptor.m_Beta = static_cast<float>(fp16Beta);
1281 }
1282 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1283 {
1284 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1285 {
1286 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1287 }
1288 }
1289 else
1290 {
1291 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1292 }
1293
1294 // Read axis
1295 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1296 {
1297 return Fail("%s: Failed to read input 2", __func__);
1298 }
1299
1300 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001301 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1302 {
1303 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1304 IsLogSoftmaxSupported,
1305 data.m_Backends,
1306 isSupported,
1307 input.GetTensorInfo(),
1308 outputInfo,
1309 descriptor);
1310 };
1311
1312 if(IsDynamicTensor(outputInfo))
1313 {
1314 isSupported = AreDynamicTensorsSupported();
1315 }
1316 else
1317 {
1318 validateFunc(outputInfo, isSupported);
1319 }
1320
Kevin May42477c12020-03-26 13:34:14 +00001321 if (!isSupported)
1322 {
1323 return false;
1324 }
1325
1326 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
1327 if (!layer)
1328 {
1329 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1330 }
1331
1332 input.Connect(layer->GetInputSlot(0));
1333
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001334 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001335}
1336
1337template<typename HalPolicy,
1338 typename HalOperation = typename HalPolicy::Operation,
1339 typename HalModel = typename HalPolicy::Model>
1340bool ConvertMaximum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1341{
1342 using HalOperand = typename HalPolicy::Operand;
1343
1344 ALOGV("HalPolicy::ConvertMaximum()");
1345
1346 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1347 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1348
1349 if (!input0.IsValid() || !input1.IsValid())
1350 {
1351 return Fail("%s: Operation has invalid inputs", __func__);
1352 }
1353
1354 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1355 if (!outputOperand)
1356 {
1357 return Fail("%s: Could not read output", __func__);
1358 }
1359
1360 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001361
1362 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001363 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
1364 {
1365 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1366 IsMaximumSupported,
1367 data.m_Backends,
1368 isSupported,
1369 input0.GetTensorInfo(),
1370 input1.GetTensorInfo(),
1371 outInfo);
1372 };
1373
1374 if(IsDynamicTensor(outInfo))
1375 {
1376 isSupported = AreDynamicTensorsSupported();
1377 }
1378 else
1379 {
1380 validateFunc(outInfo, isSupported);
1381 }
Kevin May42477c12020-03-26 13:34:14 +00001382
1383 if (!isSupported)
1384 {
1385 return false;
1386 }
1387
1388 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
1389 assert(layer != nullptr);
1390 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1391 if (!isReshapeSupported)
1392 {
1393 return false;
1394 }
1395
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001396 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001397}
1398
1399template<typename HalPolicy,
1400 typename HalOperation = typename HalPolicy::Operation,
1401 typename HalModel = typename HalPolicy::Model>
1402bool ConvertMinimum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1403{
1404 using HalOperand = typename HalPolicy::Operand;
1405
1406 ALOGV("HalPolicy::ConvertMinimum()");
1407
1408 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1409 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1410
1411 if (!input0.IsValid() || !input1.IsValid())
1412 {
1413 return Fail("%s: Operation has invalid inputs", __func__);
1414 }
1415
1416 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1417 if (!output)
1418 {
1419 return Fail("%s: Could not read output 0", __func__);
1420 }
1421
1422 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001423
1424 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001425 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1426 {
1427 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1428 IsMinimumSupported,
1429 data.m_Backends,
1430 isSupported,
1431 input0.GetTensorInfo(),
1432 input1.GetTensorInfo(),
1433 outputInfo);
1434 };
1435
1436 if(IsDynamicTensor(outputInfo))
1437 {
1438 isSupported = AreDynamicTensorsSupported();
1439 }
1440 else
1441 {
1442 validateFunc(outputInfo, isSupported);
1443 }
Kevin May42477c12020-03-26 13:34:14 +00001444
1445 if (!isSupported)
1446 {
1447 return false;
1448 }
1449
1450 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
1451 assert(layer != nullptr);
1452 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1453 if (!isReshapeSupported)
1454 {
1455 return false;
1456 }
1457
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001458 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001459}
1460
1461template<typename HalPolicy,
1462 typename HalOperation = typename HalPolicy::Operation,
1463 typename HalModel = typename HalPolicy::Model>
1464bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1465{
1466 using HalOperand = typename HalPolicy::Operand;
1467 using HalOperandType = typename HalPolicy::OperandType;
1468
1469 ALOGV("HalPolicy::ConvertPadV2()");
1470
1471 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1472 if (!input.IsValid())
1473 {
1474 return Fail("%s: Could not read input 0", __func__);
1475 }
1476
1477 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1478 if (!output)
1479 {
1480 return Fail("%s: Could not read output", __func__);
1481 }
1482
1483 const TensorInfo& inputInfo = input.GetTensorInfo();
1484 unsigned int rank = inputInfo.GetNumDimensions();
1485
1486 PadDescriptor descriptor;
1487 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1488 {
1489 return Fail("%s: Could not convert paddings", __func__);
1490 }
1491
1492 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001493
1494 // Determine type of padding value
1495 HalOperandType operandType0;
1496 HalOperandType operandType2;
1497
1498 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1499 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1500 {
1501 return Fail("%s: Operation has invalid inputs", __func__);
1502 }
1503
1504 // Read value to use for padding
1505 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1506 {
1507 Half f16PadValue;
1508 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1509 {
1510 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1511 }
1512
1513 descriptor.m_PadValue = f16PadValue;
1514 }
1515 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1516 {
1517 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1518 {
1519 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1520 }
1521 }
1522 else if (operandType0 == HalOperandType::TENSOR_QUANT8_ASYMM && operandType2 == HalOperandType::INT32)
1523 {
1524 int32_t intPadValue = 0;
1525 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1526 {
1527 return Fail("%s: Could not read input 2 (INT32)", __func__);
1528 }
1529 descriptor.m_PadValue = intPadValue;
1530 }
1531 else
1532 {
1533 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1534 }
1535
1536 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001537 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1538 {
1539 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1540 IsPadSupported,
1541 data.m_Backends,
1542 isSupported,
1543 inputInfo,
1544 outputInfo,
1545 descriptor);
1546 };
1547
1548 if(IsDynamicTensor(outputInfo))
1549 {
1550 isSupported = AreDynamicTensorsSupported();
1551 }
1552 else
1553 {
1554 validateFunc(outputInfo, isSupported);
1555 }
1556
Kevin May42477c12020-03-26 13:34:14 +00001557 if (!isSupported)
1558 {
1559 return false;
1560 }
1561
1562 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
1563 assert(layer != nullptr);
1564 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001565
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001566 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001567}
1568
1569template<typename HalPolicy,
1570 typename HalOperation = typename HalPolicy::Operation,
1571 typename HalModel = typename HalPolicy::Model>
1572bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1573{
1574 using HalOperand = typename HalPolicy::Operand;
1575
1576 ALOGV("HalPolicy::ConvertPrelu()");
1577
1578 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1579 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1580
1581 if (!input.IsValid() || !alpha.IsValid())
1582 {
1583 return Fail("%s: Operation has invalid inputs", __func__);
1584 }
1585
1586 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1587
1588 if (!output)
1589 {
1590 return Fail("%s: Could not read output", __func__);
1591 }
1592
1593 const TensorInfo& inputInfo = input.GetTensorInfo();
1594 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1595 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1596
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001597 bool isSupported = false;
1598 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001599 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001600 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1601 IsPreluSupported,
1602 data.m_Backends,
1603 isSupported,
1604 inputInfo,
1605 alphaInfo,
1606 outputInfo);
1607 };
1608
1609 if(IsDynamicTensor(outputInfo))
1610 {
1611 isSupported = AreDynamicTensorsSupported();
1612 }
1613 else
1614 {
1615 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001616 }
1617
Kevin May42477c12020-03-26 13:34:14 +00001618 if (!isSupported)
1619 {
1620 return false;
1621 }
1622
1623 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
1624
1625 if (!layer)
1626 {
1627 return Fail("%s: AddPreluLayer failed", __func__);
1628 }
1629
1630 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1631 if (!isReshapeSupported)
1632 {
1633 return false;
1634 }
1635
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001636 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001637}
1638
1639template<typename HalPolicy,
1640 typename HalOperation = typename HalPolicy::Operation,
1641 typename HalModel = typename HalPolicy::Model>
1642bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1643{
1644 using HalOperand = typename HalPolicy::Operand;
1645
1646 ALOGV("HalPolicy::ConvertQuantize()");
1647
1648 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1649 if (!input.IsValid())
1650 {
1651 return Fail("%s: Operation has invalid input", __func__);
1652 }
1653
1654 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1655 if (!outputOperand)
1656 {
1657 return Fail("%s: Operation has invalid outputs", __func__);
1658 }
1659
1660 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001661
1662 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001663 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1664 {
1665 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1666 IsQuantizeSupported,
1667 data.m_Backends,
1668 isSupported,
1669 input.GetTensorInfo(),
1670 outputInfo);
1671 };
1672
1673 if(IsDynamicTensor(outputInfo))
1674 {
1675 isSupported = AreDynamicTensorsSupported();
1676 }
1677 else
1678 {
1679 validateFunc(outputInfo, isSupported);
1680 }
1681
Kevin May42477c12020-03-26 13:34:14 +00001682 if (!isSupported)
1683 {
1684 return false;
1685 }
1686
1687 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
1688 assert(layer != nullptr);
1689 input.Connect(layer->GetInputSlot(0));
1690
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001691 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001692}
1693
1694template<typename HalPolicy,
1695 typename HalOperation = typename HalPolicy::Operation,
1696 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001697bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001698{
1699 using HalOperand = typename HalPolicy::Operand;
1700
Sadik Armagan813f2302020-05-19 14:10:30 +01001701 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001702
1703 //Inputs:
1704 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1705 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1706 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1707 if (!input.IsValid())
1708 {
1709 return Fail("%s: Could not read input 0: input", __func__);
1710 }
1711
1712 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1713 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1714 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1715 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1716 if (!previousCellStateIn.IsValid())
1717 {
1718 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1719 }
1720
1721 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1722 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1723 // is quantized with a fixed quantization range of -1, 127/128.
1724 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1725 if (!previousOutputIn.IsValid())
1726 {
1727 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1728 }
1729
1730 // Get the input tensors:
1731 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1732 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1733 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1734 const ConstTensorPin inputToInputWeightsPin =
1735 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1736
1737 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1738 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1739 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1740 const ConstTensorPin inputToForgetWeightsPin =
1741 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1742
1743 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1744 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1745 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1746 const ConstTensorPin inputToCellWeightsPin =
1747 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1748
1749 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1750 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1751 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1752 const ConstTensorPin inputToOutputWeightsPin =
1753 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1754
1755 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1756 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1757 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1758 const ConstTensorPin recurrentToInputWeightsPin =
1759 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1760
1761 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1762 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1763 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1764 const ConstTensorPin recurrentToForgetWeightsPin =
1765 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1766
1767 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1768 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1769 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1770 const ConstTensorPin recurrentToCellWeightsPin =
1771 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1772
1773 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1774 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1775 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1776 const ConstTensorPin recurrentToOutputWeightsPin =
1777 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1778
1779 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1780 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1781 // of input and weights scales and zeroPoint equal to 0.
1782 const ConstTensorPin inputGateBiasPin =
1783 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1784
1785 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1786 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1787 // of input and weights scales and zeroPoint equal to 0.
1788 const ConstTensorPin forgetGateBiasPin =
1789 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1790
1791 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1792 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1793 // and weights scales and zeroPoint equal to 0.
1794 const ConstTensorPin cellBiasPin =
1795 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1796
1797 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1798 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1799 // of input and weights scales and zeroPoint equal to 0.
1800 const ConstTensorPin outputGateBiasPin =
1801 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1802
1803 if (!inputToInputWeightsPin.IsValid() ||
1804 !inputToForgetWeightsPin.IsValid() ||
1805 !inputToCellWeightsPin.IsValid() ||
1806 !inputToOutputWeightsPin.IsValid() ||
1807 !recurrentToInputWeightsPin.IsValid() ||
1808 !recurrentToForgetWeightsPin.IsValid() ||
1809 !recurrentToCellWeightsPin.IsValid() ||
1810 !recurrentToOutputWeightsPin.IsValid() ||
1811 !inputGateBiasPin.IsValid() ||
1812 !forgetGateBiasPin.IsValid() ||
1813 !cellBiasPin.IsValid() ||
1814 !outputGateBiasPin.IsValid())
1815 {
1816 return Fail("%s: Operation has invalid tensor inputs", __func__);
1817 }
1818
1819 // Outputs:
1820 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1821 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1822 // of -2^4, 2^4 * 32767/32768.
1823 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1824 if (!cellStateOut)
1825 {
1826 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1827 }
1828
1829 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1830 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1831 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1832 if (!output)
1833 {
1834 return Fail("%s: Could not read output 1: output", __func__);
1835 }
1836
1837 // Inputs
1838 const TensorInfo& inputInfo = input.GetTensorInfo();
1839 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1840 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1841
1842 // Outputs
1843 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1844 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1845
1846 // Dynamic tensors currently not supported
1847 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1848 {
1849 return Fail("%s: Dynamic output tensors are not supported", __func__);
1850 }
1851
1852 QuantizedLstmInputParams params;
1853
1854 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1855 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1856 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1857 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1858 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1859 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1860 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1861 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1862 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1863 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1864 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1865 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1866
1867 QuantizedLstmInputParamsInfo paramsInfo;
1868 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1869 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1870 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1871 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1872 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1873 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1874 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1875 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1876 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1877 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1878 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1879 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1880
1881 bool isSupported = false;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001882 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1883 {
1884 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1885 IsQuantizedLstmSupported,
1886 data.m_Backends,
1887 isSupported,
1888 inputInfo,
1889 previousCellStateInInfo,
1890 previousOutputInInfo,
1891 cellStateOutInfo,
1892 outputInfo,
1893 paramsInfo);
1894 };
1895
1896 bool isDynamic = false;
1897 if (!IsDynamicTensor(cellStateOutInfo) &&
1898 !IsDynamicTensor(outputInfo))
1899 {
1900 validateFunc(outputInfo, isSupported);
1901 }
1902 else
1903 {
1904 isDynamic = true;
1905 isSupported = AreDynamicTensorsSupported();
1906 }
Kevin May42477c12020-03-26 13:34:14 +00001907
1908 if (!isSupported)
1909 {
1910 return false;
1911 }
1912
1913 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1914 input.Connect(layer->GetInputSlot(0));
1915 previousCellStateIn.Connect(layer->GetInputSlot(1));
1916 previousOutputIn.Connect(layer->GetInputSlot(2));
1917
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001918 if (!isDynamic)
1919 {
1920 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1921 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
1922 }
1923 else
1924 {
1925 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1926 SetupAndTrackLayerOutputSlot<HalPolicy>(
1927 operation, 1, *layer, 1, model, data, nullptr, validateFunc, true));
1928 }
1929
Kevin May42477c12020-03-26 13:34:14 +00001930}
1931
1932template<typename HalPolicy,
1933 typename HalOperation = typename HalPolicy::Operation,
1934 typename HalModel = typename HalPolicy::Model>
1935bool ConvertResize(const HalOperation& operation,
1936 const HalModel& model,
1937 ConversionData& data,
1938 ResizeMethod resizeMethod)
1939{
1940 using HalOperand = typename HalPolicy::Operand;
1941 using HalOperandType = typename HalPolicy::OperandType;
1942 ALOGV("HalPolicy::ConvertResize()");
1943 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
1944
1945 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1946 if (!input.IsValid())
1947 {
1948 return Fail("%s: Could not read input 0", __func__);
1949 }
1950
1951 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1952 if (!output)
1953 {
1954 return Fail("%s: Could not read output 0", __func__);
1955 }
1956
1957 const TensorInfo& inputInfo = input.GetTensorInfo();
1958 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1959
Kevin May42477c12020-03-26 13:34:14 +00001960 ResizeDescriptor descriptor;
1961 descriptor.m_Method = resizeMethod;
1962 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
1963
1964 HalOperandType operandType1;
1965 HalOperandType operandType2;
1966
1967 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
1968 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1969 {
1970 return Fail("%s: Operation has invalid inputs", __func__);
1971 }
1972
1973 if (operandType1 != operandType2)
1974 {
1975 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1976 }
1977
1978 if (operandType1 == HalOperandType::INT32)
1979 {
1980 // Case 1: resizing by shape
1981 int32_t targetWidth = 0;
1982 int32_t targetHeight = 0;
1983
1984 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
1985 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
1986 {
1987 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1988 }
1989
1990 if (targetWidth < 0 || targetHeight < 0)
1991 {
1992 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1993 "Target width/height cannot be < 0", __func__);
1994 }
1995
1996 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
1997 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
1998 }
1999 else if (operandType1 == HalOperandType::FLOAT32)
2000 {
2001 // Case 2: resizing by scale
2002 float widthScale = 1.0f;
2003 float heightScale = 1.0f;
2004
2005 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2006 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2007 {
2008 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2009 }
2010
2011 const TensorShape& inputShape = inputInfo.GetShape();
2012 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2013
2014 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2015 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2016
2017 descriptor.m_TargetWidth = std::floor(width * widthScale);
2018 descriptor.m_TargetHeight = std::floor(height * heightScale);
2019 }
2020 else if (operandType1 == HalOperandType::FLOAT16)
2021 {
2022 Half widthScale;
2023 Half heightScale;
2024
2025 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2026 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2027 {
2028 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2029 }
2030
2031 const TensorShape& inputShape = inputInfo.GetShape();
2032 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2033
2034 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2035 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2036
2037 descriptor.m_TargetWidth = std::floor(width * widthScale);
2038 descriptor.m_TargetHeight = std::floor(height * heightScale);
2039 }
2040 else
2041 {
2042 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2043 }
2044
David Monahanf057e6f2020-06-22 09:55:23 +01002045 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2046 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002047
Kevin May42477c12020-03-26 13:34:14 +00002048 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002049 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2050 {
2051 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2052 IsResizeSupported,
2053 data.m_Backends,
2054 isSupported,
2055 inputInfo,
2056 outputInfo,
2057 descriptor);
2058 };
2059
2060 if(IsDynamicTensor(outputInfo))
2061 {
2062 isSupported = AreDynamicTensorsSupported();
2063 }
2064 else
2065 {
2066 validateFunc(outputInfo, isSupported);
2067 }
Kevin May42477c12020-03-26 13:34:14 +00002068
2069 if (!isSupported)
2070 {
2071 return false;
2072 }
2073
2074 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Kevin May42477c12020-03-26 13:34:14 +00002075 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +00002076 input.Connect(layer->GetInputSlot(0));
2077
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002078 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002079}
2080
2081template<typename HalPolicy,
2082 typename HalOperation = typename HalPolicy::Operation,
2083 typename HalModel = typename HalPolicy::Model>
2084bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2085{
2086 using HalOperand = typename HalPolicy::Operand;
2087 using HalOperandType = typename HalPolicy::OperandType;
2088
2089 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2090
2091 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2092 if (!input.IsValid() )
2093 {
2094 return Fail("%s: Operation has invalid inputs", __func__);
2095 }
2096
2097 const TensorInfo& inputInfo = input.GetTensorInfo();
2098 unsigned int rank = inputInfo.GetNumDimensions();
2099 if (rank != 4)
2100 {
2101 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2102 }
2103
2104 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2105 if (!output)
2106 {
2107 return Fail("%s: Could not read output 0", __func__);
2108 }
2109
2110 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002111
2112 SpaceToDepthDescriptor desc;
2113
2114 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2115
2116 if (desc.m_BlockSize <= 1)
2117 {
2118 return Fail("%s: Block size must be at least 1 in all dimensions");
2119 }
2120
2121 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2122
2123 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002124 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2125 {
2126 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2127 IsSpaceToDepthSupported,
2128 data.m_Backends,
2129 isSupported,
2130 inputInfo,
2131 outputInfo,
2132 desc);
2133 };
2134
2135 if(IsDynamicTensor(outputInfo))
2136 {
2137 isSupported = AreDynamicTensorsSupported();
2138 }
2139 else
2140 {
2141 validateFunc(outputInfo, isSupported);
2142 }
2143
Kevin May42477c12020-03-26 13:34:14 +00002144 if (!isSupported)
2145 {
2146 return false;
2147 }
2148
2149 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
2150 assert(layer != nullptr);
2151 input.Connect(layer->GetInputSlot(0));
2152
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002153 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002154}
2155
2156template<typename HalPolicy,
2157 typename HalOperation = typename HalPolicy::Operation,
2158 typename HalModel = typename HalPolicy::Model>
2159bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2160{
2161 using HalOperand = typename HalPolicy::Operand;
2162 using HalOperandType = typename HalPolicy::OperandType;
2163
2164 ALOGV("HalPolicy::ConvertSoftmax()");
2165
2166 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2167 if (!input.IsValid())
2168 {
2169 return Fail("%s: Operation has invalid inputs", __func__);
2170 }
2171
2172 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2173 if (!outputOperand)
2174 {
2175 return Fail("%s: Operation has no outputs", __func__);
2176 }
2177
2178 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002179
2180 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002181 HalOperandType outputType = outputOperand->type;
2182
2183 // Read beta value
2184 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002185 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002186 Half value;
2187
2188 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2189 {
2190 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2191 }
2192
2193 desc.m_Beta = static_cast<float>(value);
2194 }
2195 else
2196 {
2197 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2198 {
2199 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2200 }
Kevin May42477c12020-03-26 13:34:14 +00002201 }
2202
2203 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002204 2,
2205 HalOperandType::INT32,
2206 desc.m_Axis,
2207 model,
2208 data))
Kevin May42477c12020-03-26 13:34:14 +00002209 {
2210 return Fail("%s: Operation has invalid inputs", __func__);
2211 }
2212
Kevin May42477c12020-03-26 13:34:14 +00002213 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002214 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2215 {
2216 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2217 IsSoftmaxSupported,
2218 data.m_Backends,
2219 isSupported,
2220 input.GetTensorInfo(),
2221 outputInfo,
2222 desc);
2223 };
2224
2225 if(IsDynamicTensor(outputInfo))
2226 {
2227 isSupported = AreDynamicTensorsSupported();
2228 }
2229 else
2230 {
2231 validateFunc(outputInfo, isSupported);
2232 }
2233
Kevin May42477c12020-03-26 13:34:14 +00002234 if (!isSupported)
2235 {
2236 return false;
2237 }
2238
2239 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
2240 assert(layer != nullptr);
2241 input.Connect(layer->GetInputSlot(0));
2242
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002243 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002244}
2245
2246template<typename HalPolicy,
2247 typename HalOperation = typename HalPolicy::Operation,
2248 typename HalModel = typename HalPolicy::Model>
2249bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2250{
2251 using HalOperand = typename HalPolicy::Operand;
2252 using HalOperandType = typename HalPolicy::OperandType;
2253
2254 ALOGV("HalPolicy::ConvertLstm()");
2255
2256 // Inputs:
2257 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2258 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2259 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2260 if (!input.IsValid())
2261 {
2262 return Fail("%s: Could not read input 0: input", __func__);
2263 }
2264 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2265 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2266 if (!outputStateIn.IsValid())
2267 {
2268 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2269 }
2270 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2271 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2272 if (!cellStateIn.IsValid())
2273 {
2274 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2275 }
2276
2277 // Get the mandatory input tensors:
2278 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2279 // [num_units, input_size].
2280 const ConstTensorPin inputToForgetWeightsPin =
2281 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2282 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2283 // [num_units, input_size].
2284 const ConstTensorPin inputToCellWeightsPin =
2285 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2286 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2287 // [num_units, input_size].
2288 const ConstTensorPin inputToOutputWeightsPin =
2289 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2290 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2291 // [num_units, output_size].
2292 const ConstTensorPin recurrentToForgetWeightsPin =
2293 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2294 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2295 // [num_units, output_size].
2296 const ConstTensorPin recurrentToCellWeightsPin =
2297 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2298 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2299 // [num_units, output_size].
2300 const ConstTensorPin recurrentToOutputWeightsPin =
2301 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2302 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2303 const ConstTensorPin forgetGateBiasPin =
2304 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2305 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2306 const ConstTensorPin cellBiasPin =
2307 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2308 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2309 const ConstTensorPin outputGateBiasPin =
2310 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2311
2312 if (!inputToForgetWeightsPin.IsValid() ||
2313 !inputToCellWeightsPin.IsValid() ||
2314 !inputToOutputWeightsPin.IsValid() ||
2315 !recurrentToForgetWeightsPin.IsValid() ||
2316 !recurrentToCellWeightsPin.IsValid() ||
2317 !recurrentToOutputWeightsPin.IsValid() ||
2318 !forgetGateBiasPin.IsValid() ||
2319 !cellBiasPin.IsValid() ||
2320 !outputGateBiasPin.IsValid())
2321 {
2322 return Fail("%s: Operation has invalid tensor inputs", __func__);
2323 }
2324
2325 // Get the optional input tensors:
2326 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2327 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2328 const ConstTensorPin inputToInputWeightsPin =
2329 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2330 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2331 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2332 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2333 const ConstTensorPin recurrentToInputWeightsPin =
2334 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2335 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2336 const ConstTensorPin cellToInputWeightsPin =
2337 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2338 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2339 const ConstTensorPin cellToForgetWeightsPin =
2340 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2341 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2342 const ConstTensorPin cellToOutputWeightsPin =
2343 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2344 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2345 const ConstTensorPin inputGateBiasPin =
2346 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2347 12,
2348 model,
2349 data,
2350 g_DontPermute,
2351 nullptr,
2352 true);
2353
2354 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2355 // [output_size, num_units].
2356 const ConstTensorPin projectionWeightsPin =
2357 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2358 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2359 const ConstTensorPin projectionBiasPin =
2360 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2361 17,
2362 model,
2363 data,
2364 g_DontPermute,
2365 nullptr,
2366 true);
2367
2368 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2369 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2370 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2371 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2372 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2373 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2374 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2375 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2376 {
2377 return Fail("%s: Operation has invalid tensor inputs", __func__);
2378 }
2379
2380 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2381 // 20: The activation function: A value indicating the activation function:
2382 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2383 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2384 // If set to 0.0 then clipping is disabled.
2385 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2386 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2387 ActivationFn activation;
2388 float cellClip;
2389 float projClip;
2390 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2391 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2392 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2393 {
2394 return Fail("%s: Operation has invalid scalar inputs", __func__);
2395 }
2396
2397 // Get the normalization tensors
2398 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2399 // Used to rescale normalized inputs to activation at input gate.
2400 const ConstTensorPin inputLayerNormWeightsPin
2401 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2402
2403 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2404 // Used to rescale normalized inputs to activation at forget gate.
2405 const ConstTensorPin forgetLayerNormWeightsPin =
2406 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2407 24,
2408 model,
2409 data,
2410 g_DontPermute,
2411 nullptr,
2412 true);
2413
2414 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2415 // Used to rescale normalized inputs to activation at cell gate.
2416 const ConstTensorPin cellLayerNormWeightsPin =
2417 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2418 25,
2419 model,
2420 data,
2421 g_DontPermute,
2422 nullptr,
2423 true);
2424
2425 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2426 // Used to rescale normalized inputs to activation at output gate.
2427 const ConstTensorPin outputLayerNormWeightsPin =
2428 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2429 26,
2430 model,
2431 data,
2432 g_DontPermute,
2433 nullptr,
2434 true);
2435
2436 // Outputs:
2437 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2438 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2439 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2440 if (!scratchBuffer)
2441 {
2442 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2443 }
2444 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2445 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2446 if (!outputStateOut)
2447 {
2448 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2449 }
2450 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2451 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2452 if (!cellStateOut)
2453 {
2454 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2455 }
2456 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2457 // effectively the same as the current “output state (out)” value.
2458 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2459 if (!output)
2460 {
2461 return Fail("%s: Could not read output 3: output", __func__);
2462 }
2463
2464 // set the params structure for the AddLstmLayer call
2465 LstmInputParams params;
2466 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2467 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2468 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2469 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2470 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2471 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2472 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2473 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2474 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2475 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2476 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2477 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2478 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2479 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2480 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2481 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2482 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2483 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2484 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2485 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2486 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2487
2488 // set the layer descriptor
2489 LstmDescriptor desc;
2490 desc.m_ActivationFunc = activation;
2491 desc.m_ClippingThresCell = cellClip;
2492 desc.m_ClippingThresProj = projClip;
2493 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2494 params.m_RecurrentToInputWeights == nullptr ||
2495 params.m_InputGateBias == nullptr);
2496 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2497 params.m_CellToOutputWeights != nullptr);
2498 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2499 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2500 params.m_ForgetLayerNormWeights != nullptr ||
2501 params.m_CellLayerNormWeights != nullptr ||
2502 params.m_OutputLayerNormWeights != nullptr);
2503
2504 // validate the optional input groups
2505 if (desc.m_CifgEnabled &&
2506 (params.m_InputToInputWeights != nullptr ||
2507 params.m_RecurrentToInputWeights != nullptr ||
2508 params.m_InputGateBias != nullptr))
2509 {
2510 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2511 " and input gate bias must be provided", __func__);
2512 }
2513
2514 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2515 {
2516 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2517 }
2518
2519 if (desc.m_PeepholeEnabled &&
2520 (params.m_CellToForgetWeights == nullptr ||
2521 params.m_CellToOutputWeights == nullptr ||
2522 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2523 {
2524 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2525 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2526 }
2527
2528 if (desc.m_LayerNormEnabled &&
2529 (params.m_ForgetLayerNormWeights == nullptr ||
2530 params.m_CellLayerNormWeights == nullptr ||
2531 params.m_OutputLayerNormWeights == nullptr ||
2532 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2533 {
2534 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2535 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2536 }
2537
2538 // Check if the layer is supported
2539 // Inputs
2540 const TensorInfo& inputInfo = input.GetTensorInfo();
2541 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2542 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2543
2544 // Outputs
2545 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2546 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2547 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2548 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2549
Kevin May42477c12020-03-26 13:34:14 +00002550 // Basic parameters
2551 LstmInputParamsInfo paramsInfo;
2552 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2553 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2554 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2555 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2556 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2557 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2558 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2559 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2560 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2561
2562 // Optional parameters
2563 if (!desc.m_CifgEnabled)
2564 {
2565 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2566 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2567 if (params.m_CellToInputWeights != nullptr)
2568 {
2569 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2570 }
2571 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2572 }
2573
2574 if (desc.m_ProjectionEnabled)
2575 {
2576 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2577 if (params.m_ProjectionBias != nullptr)
2578 {
2579 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2580 }
2581 }
2582
2583 if (desc.m_PeepholeEnabled)
2584 {
2585 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2586 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2587 }
2588
2589 if (desc.m_LayerNormEnabled)
2590 {
2591 if(!desc.m_CifgEnabled)
2592 {
2593 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2594 }
2595 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2596 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2597 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2598 }
2599
2600 bool isSupported = false;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002601 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2602 {
2603 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2604 IsLstmSupported,
2605 data.m_Backends,
2606 isSupported,
2607 inputInfo,
2608 outputStateInInfo,
2609 cellStateInInfo,
2610 scratchBufferInfo,
2611 outputStateOutInfo,
2612 cellStateOutInfo,
2613 outputInfo,
2614 desc,
2615 paramsInfo);
2616 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002617
Sadik Armagandbda4b72020-09-03 11:33:07 +01002618 bool isDynamic = false;
2619 if (!IsDynamicTensor(outputStateOutInfo) &&
2620 !IsDynamicTensor(scratchBufferInfo) &&
2621 !IsDynamicTensor(cellStateOutInfo) &&
2622 !IsDynamicTensor(outputInfo))
2623 {
2624 validateFunc(outputInfo, isSupported);
2625 }
2626 else
2627 {
2628 isDynamic = true;
2629 isSupported = AreDynamicTensorsSupported();
2630 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002631
Kevin May42477c12020-03-26 13:34:14 +00002632 if (!isSupported)
2633 {
2634 return false;
2635 }
2636
2637 // Add the layer
2638 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2639
2640 input.Connect(layer->GetInputSlot(0));
2641 outputStateIn.Connect(layer->GetInputSlot(1));
2642 cellStateIn.Connect(layer->GetInputSlot(2));
2643
Sadik Armagandbda4b72020-09-03 11:33:07 +01002644 if (!isDynamic)
2645 {
2646 return (
2647 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2648 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2649 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2650 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2651 }
2652 else
2653 {
2654 return (
2655 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2656 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2657 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2658 SetupAndTrackLayerOutputSlot<HalPolicy>(
2659 operation, 3, *layer, 3, model, data, nullptr, validateFunc, true));
2660 }
2661
Kevin May42477c12020-03-26 13:34:14 +00002662}
2663
2664template<typename HalPolicy,
2665 typename HalOperation = typename HalPolicy::Operation,
2666 typename HalModel = typename HalPolicy::Model>
2667bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2668{
2669 using HalOperand = typename HalPolicy::Operand;
2670 using HalOperandType = typename HalPolicy::OperandType;
2671
2672 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2673
2674 if (!input.IsValid())
2675 {
2676 return Fail("%s: Operation has invalid inputs", __func__);
2677 }
2678
2679 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2680
2681 if (!output)
2682 {
2683 return Fail("%s: Could not read output 0", __func__);
2684 }
2685
2686 const TensorInfo& inputInfo = input.GetTensorInfo();
2687 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002688
2689 // ArmNN does not currently support non-fixed weights or bias
2690 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2691 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2692
2693 if (weightsOperand == nullptr)
2694 {
2695 return Fail("%s: Operand is invalid", __func__);
2696 }
2697 TransposeConvolution2dDescriptor desc;
2698 desc.m_DataLayout = DataLayout::NHWC;
2699
2700 // Determine whether padding is implicit or explicit
2701 bool implicitPadding = operation.inputs.size() == 9;
2702
2703 if (implicitPadding )
2704 {
2705 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2706 }
2707 else
2708 {
2709 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2710 }
2711
2712 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2713 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2714 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2715
2716 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2717
2718 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2719 // We have to permute it to OIHW if the data layout is NCHW.
2720 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2721 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2722 model, data, OHWIToOIHW) :
2723 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2724
2725 // Bias is a 1D tensor
2726 const ConstTensorPin biasPin =
2727 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2728
2729 if (!weightsPin.IsValid())
2730 {
2731 return Fail("%s: Operation has invalid weights", __func__);
2732 }
2733
2734 if (!biasPin.IsValid())
2735 {
2736 return Fail("%s: Operation has invalid biases", __func__);
2737 }
2738
2739 ConstTensor weights = weightsPin.GetConstTensor();
2740 ConstTensor bias = biasPin.GetConstTensor();
2741 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2742
2743 ActivationFn activation;
2744
2745 if (implicitPadding)
2746 {
2747 int32_t strideX{0};
2748 int32_t strideY{0};
2749 int32_t padLeft{0};
2750 int32_t padRight{0};
2751 int32_t padTop{0};
2752 int32_t padBottom{0};
2753
2754 android::nn::PaddingScheme paddingScheme;
2755 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2756 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2757 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2758 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2759 {
2760 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2761 }
2762
2763 const uint32_t kernelX = weights.GetShape()[widthIndex];
2764 const uint32_t kernelY = weights.GetShape()[heightIndex];
2765 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2766 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
2767
2768 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2769 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
2770
2771 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2772 // but Arm NN only supports values >= 0
2773 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2774 {
2775 return Fail("%s: Negative padding values are not supported", __func__);
2776 }
2777
Colm Donelan8f3d33e2020-07-06 15:41:43 +01002778 // If output shape has been specified as a parameter then extract it and make it available.
2779 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
2780 std::vector<int32_t> outputShape;
2781 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
2782 {
2783 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
2784 for (int dimension : outputShape)
2785 {
2786 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
2787 }
2788 desc.m_OutputShapeEnabled = true;
2789 }
2790
Kevin May42477c12020-03-26 13:34:14 +00002791 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2792 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
2793 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2794 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2795 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2796 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
2797 }
2798 else if (operation.inputs.size() == 11)
2799 {
2800 // explicit padding
2801 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2802 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2803 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2804 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2805 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2806 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
2807 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
2808 {
2809 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2810 }
2811 }
2812 else
2813 {
2814 return Fail("%s: Unsupported number of operation inputs", __func__);
2815 }
2816
2817 desc.m_BiasEnabled = true;
2818 Optional<TensorInfo> biases(bias.GetInfo());
2819
2820 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002821 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2822 {
2823 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2824 IsTransposeConvolution2dSupported,
2825 data.m_Backends,
2826 isSupported,
2827 inputInfo,
2828 outputInfo,
2829 desc,
2830 weights.GetInfo(),
2831 biases);
2832 };
2833
2834 if(IsDynamicTensor(outputInfo))
2835 {
2836 isSupported = AreDynamicTensorsSupported();
2837 }
2838 else
2839 {
2840 validateFunc(outputInfo, isSupported);
2841 }
Kevin May42477c12020-03-26 13:34:14 +00002842 if (!isSupported)
2843 {
2844 return false;
2845 }
2846
2847 IConnectableLayer* startLayer =
2848 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
2849 if (!startLayer)
2850 {
2851 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2852 }
2853
2854 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2855 if (!endLayer)
2856 {
2857 return Fail("%s: ProcessActivation failed", __func__);
2858 }
2859
2860 input.Connect(startLayer->GetInputSlot(0));
2861
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002862 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002863}
2864
2865} // armnn_driver namespace