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