blob: 210caa1b4fda43a717dd9b94d82656ca72ba4567 [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;
1882 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1883 IsQuantizedLstmSupported,
1884 data.m_Backends,
1885 isSupported,
1886 inputInfo,
1887 previousCellStateInInfo,
1888 previousOutputInInfo,
1889 cellStateOutInfo,
1890 outputInfo,
1891 paramsInfo);
1892
1893 if (!isSupported)
1894 {
1895 return false;
1896 }
1897
1898 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1899 input.Connect(layer->GetInputSlot(0));
1900 previousCellStateIn.Connect(layer->GetInputSlot(1));
1901 previousOutputIn.Connect(layer->GetInputSlot(2));
1902
1903 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1904 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
1905}
1906
1907template<typename HalPolicy,
1908 typename HalOperation = typename HalPolicy::Operation,
1909 typename HalModel = typename HalPolicy::Model>
1910bool ConvertResize(const HalOperation& operation,
1911 const HalModel& model,
1912 ConversionData& data,
1913 ResizeMethod resizeMethod)
1914{
1915 using HalOperand = typename HalPolicy::Operand;
1916 using HalOperandType = typename HalPolicy::OperandType;
1917 ALOGV("HalPolicy::ConvertResize()");
1918 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
1919
1920 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1921 if (!input.IsValid())
1922 {
1923 return Fail("%s: Could not read input 0", __func__);
1924 }
1925
1926 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1927 if (!output)
1928 {
1929 return Fail("%s: Could not read output 0", __func__);
1930 }
1931
1932 const TensorInfo& inputInfo = input.GetTensorInfo();
1933 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1934
Kevin May42477c12020-03-26 13:34:14 +00001935 ResizeDescriptor descriptor;
1936 descriptor.m_Method = resizeMethod;
1937 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
1938
1939 HalOperandType operandType1;
1940 HalOperandType operandType2;
1941
1942 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
1943 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1944 {
1945 return Fail("%s: Operation has invalid inputs", __func__);
1946 }
1947
1948 if (operandType1 != operandType2)
1949 {
1950 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1951 }
1952
1953 if (operandType1 == HalOperandType::INT32)
1954 {
1955 // Case 1: resizing by shape
1956 int32_t targetWidth = 0;
1957 int32_t targetHeight = 0;
1958
1959 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
1960 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
1961 {
1962 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1963 }
1964
1965 if (targetWidth < 0 || targetHeight < 0)
1966 {
1967 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1968 "Target width/height cannot be < 0", __func__);
1969 }
1970
1971 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
1972 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
1973 }
1974 else if (operandType1 == HalOperandType::FLOAT32)
1975 {
1976 // Case 2: resizing by scale
1977 float widthScale = 1.0f;
1978 float heightScale = 1.0f;
1979
1980 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
1981 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
1982 {
1983 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1984 }
1985
1986 const TensorShape& inputShape = inputInfo.GetShape();
1987 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1988
1989 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1990 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1991
1992 descriptor.m_TargetWidth = std::floor(width * widthScale);
1993 descriptor.m_TargetHeight = std::floor(height * heightScale);
1994 }
1995 else if (operandType1 == HalOperandType::FLOAT16)
1996 {
1997 Half widthScale;
1998 Half heightScale;
1999
2000 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2001 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2002 {
2003 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2004 }
2005
2006 const TensorShape& inputShape = inputInfo.GetShape();
2007 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2008
2009 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2010 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2011
2012 descriptor.m_TargetWidth = std::floor(width * widthScale);
2013 descriptor.m_TargetHeight = std::floor(height * heightScale);
2014 }
2015 else
2016 {
2017 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2018 }
2019
David Monahanf057e6f2020-06-22 09:55:23 +01002020 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2021 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002022
Kevin May42477c12020-03-26 13:34:14 +00002023 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002024 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2025 {
2026 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2027 IsResizeSupported,
2028 data.m_Backends,
2029 isSupported,
2030 inputInfo,
2031 outputInfo,
2032 descriptor);
2033 };
2034
2035 if(IsDynamicTensor(outputInfo))
2036 {
2037 isSupported = AreDynamicTensorsSupported();
2038 }
2039 else
2040 {
2041 validateFunc(outputInfo, isSupported);
2042 }
Kevin May42477c12020-03-26 13:34:14 +00002043
2044 if (!isSupported)
2045 {
2046 return false;
2047 }
2048
2049 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Kevin May42477c12020-03-26 13:34:14 +00002050 assert(layer != nullptr);
Kevin May42477c12020-03-26 13:34:14 +00002051 input.Connect(layer->GetInputSlot(0));
2052
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002053 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002054}
2055
2056template<typename HalPolicy,
2057 typename HalOperation = typename HalPolicy::Operation,
2058 typename HalModel = typename HalPolicy::Model>
2059bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2060{
2061 using HalOperand = typename HalPolicy::Operand;
2062 using HalOperandType = typename HalPolicy::OperandType;
2063
2064 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2065
2066 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2067 if (!input.IsValid() )
2068 {
2069 return Fail("%s: Operation has invalid inputs", __func__);
2070 }
2071
2072 const TensorInfo& inputInfo = input.GetTensorInfo();
2073 unsigned int rank = inputInfo.GetNumDimensions();
2074 if (rank != 4)
2075 {
2076 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2077 }
2078
2079 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2080 if (!output)
2081 {
2082 return Fail("%s: Could not read output 0", __func__);
2083 }
2084
2085 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002086
2087 SpaceToDepthDescriptor desc;
2088
2089 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2090
2091 if (desc.m_BlockSize <= 1)
2092 {
2093 return Fail("%s: Block size must be at least 1 in all dimensions");
2094 }
2095
2096 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2097
2098 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002099 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2100 {
2101 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2102 IsSpaceToDepthSupported,
2103 data.m_Backends,
2104 isSupported,
2105 inputInfo,
2106 outputInfo,
2107 desc);
2108 };
2109
2110 if(IsDynamicTensor(outputInfo))
2111 {
2112 isSupported = AreDynamicTensorsSupported();
2113 }
2114 else
2115 {
2116 validateFunc(outputInfo, isSupported);
2117 }
2118
Kevin May42477c12020-03-26 13:34:14 +00002119 if (!isSupported)
2120 {
2121 return false;
2122 }
2123
2124 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
2125 assert(layer != nullptr);
2126 input.Connect(layer->GetInputSlot(0));
2127
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002128 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002129}
2130
2131template<typename HalPolicy,
2132 typename HalOperation = typename HalPolicy::Operation,
2133 typename HalModel = typename HalPolicy::Model>
2134bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2135{
2136 using HalOperand = typename HalPolicy::Operand;
2137 using HalOperandType = typename HalPolicy::OperandType;
2138
2139 ALOGV("HalPolicy::ConvertSoftmax()");
2140
2141 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2142 if (!input.IsValid())
2143 {
2144 return Fail("%s: Operation has invalid inputs", __func__);
2145 }
2146
2147 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2148 if (!outputOperand)
2149 {
2150 return Fail("%s: Operation has no outputs", __func__);
2151 }
2152
2153 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002154
2155 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002156 HalOperandType outputType = outputOperand->type;
2157
2158 // Read beta value
2159 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002160 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002161 Half value;
2162
2163 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2164 {
2165 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2166 }
2167
2168 desc.m_Beta = static_cast<float>(value);
2169 }
2170 else
2171 {
2172 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2173 {
2174 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2175 }
Kevin May42477c12020-03-26 13:34:14 +00002176 }
2177
2178 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002179 2,
2180 HalOperandType::INT32,
2181 desc.m_Axis,
2182 model,
2183 data))
Kevin May42477c12020-03-26 13:34:14 +00002184 {
2185 return Fail("%s: Operation has invalid inputs", __func__);
2186 }
2187
Kevin May42477c12020-03-26 13:34:14 +00002188 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002189 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2190 {
2191 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2192 IsSoftmaxSupported,
2193 data.m_Backends,
2194 isSupported,
2195 input.GetTensorInfo(),
2196 outputInfo,
2197 desc);
2198 };
2199
2200 if(IsDynamicTensor(outputInfo))
2201 {
2202 isSupported = AreDynamicTensorsSupported();
2203 }
2204 else
2205 {
2206 validateFunc(outputInfo, isSupported);
2207 }
2208
Kevin May42477c12020-03-26 13:34:14 +00002209 if (!isSupported)
2210 {
2211 return false;
2212 }
2213
2214 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
2215 assert(layer != nullptr);
2216 input.Connect(layer->GetInputSlot(0));
2217
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002218 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002219}
2220
2221template<typename HalPolicy,
2222 typename HalOperation = typename HalPolicy::Operation,
2223 typename HalModel = typename HalPolicy::Model>
2224bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2225{
2226 using HalOperand = typename HalPolicy::Operand;
2227 using HalOperandType = typename HalPolicy::OperandType;
2228
2229 ALOGV("HalPolicy::ConvertLstm()");
2230
2231 // Inputs:
2232 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2233 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2234 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2235 if (!input.IsValid())
2236 {
2237 return Fail("%s: Could not read input 0: input", __func__);
2238 }
2239 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2240 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2241 if (!outputStateIn.IsValid())
2242 {
2243 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2244 }
2245 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2246 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2247 if (!cellStateIn.IsValid())
2248 {
2249 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2250 }
2251
2252 // Get the mandatory input tensors:
2253 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2254 // [num_units, input_size].
2255 const ConstTensorPin inputToForgetWeightsPin =
2256 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2257 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2258 // [num_units, input_size].
2259 const ConstTensorPin inputToCellWeightsPin =
2260 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2261 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2262 // [num_units, input_size].
2263 const ConstTensorPin inputToOutputWeightsPin =
2264 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2265 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2266 // [num_units, output_size].
2267 const ConstTensorPin recurrentToForgetWeightsPin =
2268 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2269 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2270 // [num_units, output_size].
2271 const ConstTensorPin recurrentToCellWeightsPin =
2272 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2273 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2274 // [num_units, output_size].
2275 const ConstTensorPin recurrentToOutputWeightsPin =
2276 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2277 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2278 const ConstTensorPin forgetGateBiasPin =
2279 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2280 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2281 const ConstTensorPin cellBiasPin =
2282 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2283 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2284 const ConstTensorPin outputGateBiasPin =
2285 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2286
2287 if (!inputToForgetWeightsPin.IsValid() ||
2288 !inputToCellWeightsPin.IsValid() ||
2289 !inputToOutputWeightsPin.IsValid() ||
2290 !recurrentToForgetWeightsPin.IsValid() ||
2291 !recurrentToCellWeightsPin.IsValid() ||
2292 !recurrentToOutputWeightsPin.IsValid() ||
2293 !forgetGateBiasPin.IsValid() ||
2294 !cellBiasPin.IsValid() ||
2295 !outputGateBiasPin.IsValid())
2296 {
2297 return Fail("%s: Operation has invalid tensor inputs", __func__);
2298 }
2299
2300 // Get the optional input tensors:
2301 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2302 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2303 const ConstTensorPin inputToInputWeightsPin =
2304 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2305 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2306 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2307 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2308 const ConstTensorPin recurrentToInputWeightsPin =
2309 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2310 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2311 const ConstTensorPin cellToInputWeightsPin =
2312 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2313 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2314 const ConstTensorPin cellToForgetWeightsPin =
2315 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2316 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2317 const ConstTensorPin cellToOutputWeightsPin =
2318 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2319 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2320 const ConstTensorPin inputGateBiasPin =
2321 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2322 12,
2323 model,
2324 data,
2325 g_DontPermute,
2326 nullptr,
2327 true);
2328
2329 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2330 // [output_size, num_units].
2331 const ConstTensorPin projectionWeightsPin =
2332 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2333 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2334 const ConstTensorPin projectionBiasPin =
2335 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2336 17,
2337 model,
2338 data,
2339 g_DontPermute,
2340 nullptr,
2341 true);
2342
2343 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2344 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2345 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2346 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2347 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2348 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2349 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2350 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2351 {
2352 return Fail("%s: Operation has invalid tensor inputs", __func__);
2353 }
2354
2355 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2356 // 20: The activation function: A value indicating the activation function:
2357 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2358 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2359 // If set to 0.0 then clipping is disabled.
2360 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2361 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2362 ActivationFn activation;
2363 float cellClip;
2364 float projClip;
2365 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2366 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2367 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2368 {
2369 return Fail("%s: Operation has invalid scalar inputs", __func__);
2370 }
2371
2372 // Get the normalization tensors
2373 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2374 // Used to rescale normalized inputs to activation at input gate.
2375 const ConstTensorPin inputLayerNormWeightsPin
2376 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2377
2378 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2379 // Used to rescale normalized inputs to activation at forget gate.
2380 const ConstTensorPin forgetLayerNormWeightsPin =
2381 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2382 24,
2383 model,
2384 data,
2385 g_DontPermute,
2386 nullptr,
2387 true);
2388
2389 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2390 // Used to rescale normalized inputs to activation at cell gate.
2391 const ConstTensorPin cellLayerNormWeightsPin =
2392 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2393 25,
2394 model,
2395 data,
2396 g_DontPermute,
2397 nullptr,
2398 true);
2399
2400 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2401 // Used to rescale normalized inputs to activation at output gate.
2402 const ConstTensorPin outputLayerNormWeightsPin =
2403 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2404 26,
2405 model,
2406 data,
2407 g_DontPermute,
2408 nullptr,
2409 true);
2410
2411 // Outputs:
2412 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2413 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2414 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2415 if (!scratchBuffer)
2416 {
2417 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2418 }
2419 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2420 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2421 if (!outputStateOut)
2422 {
2423 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2424 }
2425 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2426 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2427 if (!cellStateOut)
2428 {
2429 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2430 }
2431 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2432 // effectively the same as the current “output state (out)” value.
2433 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2434 if (!output)
2435 {
2436 return Fail("%s: Could not read output 3: output", __func__);
2437 }
2438
2439 // set the params structure for the AddLstmLayer call
2440 LstmInputParams params;
2441 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2442 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2443 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2444 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2445 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2446 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2447 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2448 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2449 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2450 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2451 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2452 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2453 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2454 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2455 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2456 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2457 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2458 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2459 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2460 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2461 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2462
2463 // set the layer descriptor
2464 LstmDescriptor desc;
2465 desc.m_ActivationFunc = activation;
2466 desc.m_ClippingThresCell = cellClip;
2467 desc.m_ClippingThresProj = projClip;
2468 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2469 params.m_RecurrentToInputWeights == nullptr ||
2470 params.m_InputGateBias == nullptr);
2471 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2472 params.m_CellToOutputWeights != nullptr);
2473 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2474 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2475 params.m_ForgetLayerNormWeights != nullptr ||
2476 params.m_CellLayerNormWeights != nullptr ||
2477 params.m_OutputLayerNormWeights != nullptr);
2478
2479 // validate the optional input groups
2480 if (desc.m_CifgEnabled &&
2481 (params.m_InputToInputWeights != nullptr ||
2482 params.m_RecurrentToInputWeights != nullptr ||
2483 params.m_InputGateBias != nullptr))
2484 {
2485 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2486 " and input gate bias must be provided", __func__);
2487 }
2488
2489 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2490 {
2491 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2492 }
2493
2494 if (desc.m_PeepholeEnabled &&
2495 (params.m_CellToForgetWeights == nullptr ||
2496 params.m_CellToOutputWeights == nullptr ||
2497 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2498 {
2499 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2500 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2501 }
2502
2503 if (desc.m_LayerNormEnabled &&
2504 (params.m_ForgetLayerNormWeights == nullptr ||
2505 params.m_CellLayerNormWeights == nullptr ||
2506 params.m_OutputLayerNormWeights == nullptr ||
2507 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2508 {
2509 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2510 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2511 }
2512
2513 // Check if the layer is supported
2514 // Inputs
2515 const TensorInfo& inputInfo = input.GetTensorInfo();
2516 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2517 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2518
2519 // Outputs
2520 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2521 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2522 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2523 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2524
2525 // Check if the scratch buffer shape was initialized,
2526 // In some cases the shape could be (0,0) which requires the driver
2527 // to infer the shape and set it up accordingly.
2528 // The code below does that.
2529 TensorInfo fixSbInfo = scratchBufferInfo;
2530 if (IsDynamicTensor(scratchBufferInfo))
2531 {
2532 auto & s = fixSbInfo.GetShape();
2533 s[0] = outputStateInInfo.GetShape()[0];
2534 if (desc.m_CifgEnabled)
2535 {
2536 // 2D tensor with dimensions [num_units * 3, batch_size] with CIFG
2537 s[1] = cellStateOutInfo.GetShape()[1]*3;
2538 }
2539 else
2540 {
2541 // scratch_buffer [num_units * 4, batch_size] without CIFG
2542 s[1] = cellStateOutInfo.GetShape()[1]*4;
2543 }
2544 }
2545
2546 if (IsDynamicTensor(outputStateOutInfo) ||
2547 IsDynamicTensor(cellStateOutInfo) ||
2548 IsDynamicTensor(outputInfo))
2549 {
2550 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2551 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2552 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
2553 }
2554
2555 // Basic parameters
2556 LstmInputParamsInfo paramsInfo;
2557 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2558 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2559 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2560 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2561 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2562 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2563 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2564 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2565 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2566
2567 // Optional parameters
2568 if (!desc.m_CifgEnabled)
2569 {
2570 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2571 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2572 if (params.m_CellToInputWeights != nullptr)
2573 {
2574 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2575 }
2576 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2577 }
2578
2579 if (desc.m_ProjectionEnabled)
2580 {
2581 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2582 if (params.m_ProjectionBias != nullptr)
2583 {
2584 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2585 }
2586 }
2587
2588 if (desc.m_PeepholeEnabled)
2589 {
2590 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2591 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2592 }
2593
2594 if (desc.m_LayerNormEnabled)
2595 {
2596 if(!desc.m_CifgEnabled)
2597 {
2598 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2599 }
2600 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2601 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2602 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2603 }
2604
2605 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002606
Kevin May42477c12020-03-26 13:34:14 +00002607 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2608 IsLstmSupported,
2609 data.m_Backends,
2610 isSupported,
2611 inputInfo,
2612 outputStateInInfo,
2613 cellStateInInfo,
2614 fixSbInfo,
2615 outputStateOutInfo,
2616 cellStateOutInfo,
2617 outputInfo,
2618 desc,
2619 paramsInfo);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002620
Kevin May42477c12020-03-26 13:34:14 +00002621 if (!isSupported)
2622 {
2623 return false;
2624 }
2625
2626 // Add the layer
2627 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2628
2629 input.Connect(layer->GetInputSlot(0));
2630 outputStateIn.Connect(layer->GetInputSlot(1));
2631 cellStateIn.Connect(layer->GetInputSlot(2));
2632
Kevin May42477c12020-03-26 13:34:14 +00002633 return (
2634 (IsDynamicTensor(scratchBufferInfo)?
2635 SetupAndTrackLayerOutputSlotAndOverrideTensorInfo<HalPolicy>(
2636 operation, 0, *layer, 0, model, data,fixSbInfo):
2637 SetupAndTrackLayerOutputSlot<HalPolicy>(
2638 operation, 0, *layer, 0, model, data)) &&
2639 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2640 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2641 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2642}
2643
2644template<typename HalPolicy,
2645 typename HalOperation = typename HalPolicy::Operation,
2646 typename HalModel = typename HalPolicy::Model>
2647bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2648{
2649 using HalOperand = typename HalPolicy::Operand;
2650 using HalOperandType = typename HalPolicy::OperandType;
2651
2652 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2653
2654 if (!input.IsValid())
2655 {
2656 return Fail("%s: Operation has invalid inputs", __func__);
2657 }
2658
2659 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2660
2661 if (!output)
2662 {
2663 return Fail("%s: Could not read output 0", __func__);
2664 }
2665
2666 const TensorInfo& inputInfo = input.GetTensorInfo();
2667 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002668
2669 // ArmNN does not currently support non-fixed weights or bias
2670 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2671 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2672
2673 if (weightsOperand == nullptr)
2674 {
2675 return Fail("%s: Operand is invalid", __func__);
2676 }
2677 TransposeConvolution2dDescriptor desc;
2678 desc.m_DataLayout = DataLayout::NHWC;
2679
2680 // Determine whether padding is implicit or explicit
2681 bool implicitPadding = operation.inputs.size() == 9;
2682
2683 if (implicitPadding )
2684 {
2685 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2686 }
2687 else
2688 {
2689 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2690 }
2691
2692 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2693 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2694 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2695
2696 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2697
2698 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2699 // We have to permute it to OIHW if the data layout is NCHW.
2700 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2701 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2702 model, data, OHWIToOIHW) :
2703 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2704
2705 // Bias is a 1D tensor
2706 const ConstTensorPin biasPin =
2707 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2708
2709 if (!weightsPin.IsValid())
2710 {
2711 return Fail("%s: Operation has invalid weights", __func__);
2712 }
2713
2714 if (!biasPin.IsValid())
2715 {
2716 return Fail("%s: Operation has invalid biases", __func__);
2717 }
2718
2719 ConstTensor weights = weightsPin.GetConstTensor();
2720 ConstTensor bias = biasPin.GetConstTensor();
2721 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2722
2723 ActivationFn activation;
2724
2725 if (implicitPadding)
2726 {
2727 int32_t strideX{0};
2728 int32_t strideY{0};
2729 int32_t padLeft{0};
2730 int32_t padRight{0};
2731 int32_t padTop{0};
2732 int32_t padBottom{0};
2733
2734 android::nn::PaddingScheme paddingScheme;
2735 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2736 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2737 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2738 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2739 {
2740 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2741 }
2742
2743 const uint32_t kernelX = weights.GetShape()[widthIndex];
2744 const uint32_t kernelY = weights.GetShape()[heightIndex];
2745 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2746 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
2747
2748 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2749 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
2750
2751 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2752 // but Arm NN only supports values >= 0
2753 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2754 {
2755 return Fail("%s: Negative padding values are not supported", __func__);
2756 }
2757
Colm Donelan8f3d33e2020-07-06 15:41:43 +01002758 // If output shape has been specified as a parameter then extract it and make it available.
2759 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
2760 std::vector<int32_t> outputShape;
2761 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
2762 {
2763 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
2764 for (int dimension : outputShape)
2765 {
2766 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
2767 }
2768 desc.m_OutputShapeEnabled = true;
2769 }
2770
Kevin May42477c12020-03-26 13:34:14 +00002771 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2772 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
2773 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2774 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2775 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2776 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
2777 }
2778 else if (operation.inputs.size() == 11)
2779 {
2780 // explicit padding
2781 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2782 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2783 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2784 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2785 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2786 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
2787 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
2788 {
2789 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2790 }
2791 }
2792 else
2793 {
2794 return Fail("%s: Unsupported number of operation inputs", __func__);
2795 }
2796
2797 desc.m_BiasEnabled = true;
2798 Optional<TensorInfo> biases(bias.GetInfo());
2799
2800 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002801 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2802 {
2803 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2804 IsTransposeConvolution2dSupported,
2805 data.m_Backends,
2806 isSupported,
2807 inputInfo,
2808 outputInfo,
2809 desc,
2810 weights.GetInfo(),
2811 biases);
2812 };
2813
2814 if(IsDynamicTensor(outputInfo))
2815 {
2816 isSupported = AreDynamicTensorsSupported();
2817 }
2818 else
2819 {
2820 validateFunc(outputInfo, isSupported);
2821 }
Kevin May42477c12020-03-26 13:34:14 +00002822 if (!isSupported)
2823 {
2824 return false;
2825 }
2826
2827 IConnectableLayer* startLayer =
2828 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
2829 if (!startLayer)
2830 {
2831 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2832 }
2833
2834 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2835 if (!endLayer)
2836 {
2837 return Fail("%s: ProcessActivation failed", __func__);
2838 }
2839
2840 input.Connect(startLayer->GetInputSlot(0));
2841
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002842 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002843}
2844
2845} // armnn_driver namespace