blob: 0ad50f313629999ff8bbbffc549871e80215eab0 [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 Monahanf057e6f2020-06-22 09:55:23 +01001905 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
1906 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01001907
Kevin May42477c12020-03-26 13:34:14 +00001908 bool isSupported = false;
1909 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1910 IsResizeSupported,
1911 data.m_Backends,
1912 isSupported,
1913 inputInfo,
1914 outputInfo,
1915 descriptor);
1916
1917 if (!isSupported)
1918 {
1919 return false;
1920 }
1921
1922 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
1923
1924 assert(layer != nullptr);
1925
1926 input.Connect(layer->GetInputSlot(0));
1927
1928 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1929}
1930
1931template<typename HalPolicy,
1932 typename HalOperation = typename HalPolicy::Operation,
1933 typename HalModel = typename HalPolicy::Model>
1934bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
1935{
1936 using HalOperand = typename HalPolicy::Operand;
1937 using HalOperandType = typename HalPolicy::OperandType;
1938
1939 ALOGV("HalPolicy::ConvertSpaceToDepth()");
1940
1941 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1942 if (!input.IsValid() )
1943 {
1944 return Fail("%s: Operation has invalid inputs", __func__);
1945 }
1946
1947 const TensorInfo& inputInfo = input.GetTensorInfo();
1948 unsigned int rank = inputInfo.GetNumDimensions();
1949 if (rank != 4)
1950 {
1951 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1952 }
1953
1954 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1955 if (!output)
1956 {
1957 return Fail("%s: Could not read output 0", __func__);
1958 }
1959
1960 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1961 if (IsDynamicTensor(outputInfo))
1962 {
1963 return Fail("%s: Dynamic output tensors are not supported", __func__);
1964 }
1965
1966 SpaceToDepthDescriptor desc;
1967
1968 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
1969
1970 if (desc.m_BlockSize <= 1)
1971 {
1972 return Fail("%s: Block size must be at least 1 in all dimensions");
1973 }
1974
1975 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
1976
1977 bool isSupported = false;
1978 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1979 IsSpaceToDepthSupported,
1980 data.m_Backends,
1981 isSupported,
1982 inputInfo,
1983 outputInfo,
1984 desc);
1985 if (!isSupported)
1986 {
1987 return false;
1988 }
1989
1990 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
1991 assert(layer != nullptr);
1992 input.Connect(layer->GetInputSlot(0));
1993
1994 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1995}
1996
1997template<typename HalPolicy,
1998 typename HalOperation = typename HalPolicy::Operation,
1999 typename HalModel = typename HalPolicy::Model>
2000bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2001{
2002 using HalOperand = typename HalPolicy::Operand;
2003 using HalOperandType = typename HalPolicy::OperandType;
2004
2005 ALOGV("HalPolicy::ConvertSoftmax()");
2006
2007 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2008 if (!input.IsValid())
2009 {
2010 return Fail("%s: Operation has invalid inputs", __func__);
2011 }
2012
2013 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2014 if (!outputOperand)
2015 {
2016 return Fail("%s: Operation has no outputs", __func__);
2017 }
2018
2019 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2020 if (IsDynamicTensor(outputInfo))
2021 {
2022 return Fail("%s: Dynamic output tensors are not supported", __func__);
2023 }
2024
2025 SoftmaxDescriptor desc;
2026 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2027 {
2028 return Fail("%s: Operation has invalid inputs", __func__);
2029 }
2030
2031 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
2032 2,
2033 HalOperandType::INT32,
2034 desc.m_Axis,
2035 model,
2036 data))
2037 {
2038 return Fail("%s: Operation has invalid inputs", __func__);
2039 }
2040
2041 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2042 !(desc.m_Axis == 1 ||
2043 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2044 {
2045 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2046 }
2047
2048 bool isSupported = false;
2049 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2050 IsSoftmaxSupported,
2051 data.m_Backends,
2052 isSupported,
2053 input.GetTensorInfo(),
2054 outputInfo,
2055 desc);
2056 if (!isSupported)
2057 {
2058 return false;
2059 }
2060
2061 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
2062 assert(layer != nullptr);
2063 input.Connect(layer->GetInputSlot(0));
2064
2065 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2066}
2067
2068template<typename HalPolicy,
2069 typename HalOperation = typename HalPolicy::Operation,
2070 typename HalModel = typename HalPolicy::Model>
2071bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2072{
2073 using HalOperand = typename HalPolicy::Operand;
2074 using HalOperandType = typename HalPolicy::OperandType;
2075
2076 ALOGV("HalPolicy::ConvertLstm()");
2077
2078 // Inputs:
2079 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2080 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2081 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2082 if (!input.IsValid())
2083 {
2084 return Fail("%s: Could not read input 0: input", __func__);
2085 }
2086 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2087 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2088 if (!outputStateIn.IsValid())
2089 {
2090 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2091 }
2092 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2093 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2094 if (!cellStateIn.IsValid())
2095 {
2096 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2097 }
2098
2099 // Get the mandatory input tensors:
2100 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2101 // [num_units, input_size].
2102 const ConstTensorPin inputToForgetWeightsPin =
2103 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2104 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2105 // [num_units, input_size].
2106 const ConstTensorPin inputToCellWeightsPin =
2107 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2108 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2109 // [num_units, input_size].
2110 const ConstTensorPin inputToOutputWeightsPin =
2111 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2112 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2113 // [num_units, output_size].
2114 const ConstTensorPin recurrentToForgetWeightsPin =
2115 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2116 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2117 // [num_units, output_size].
2118 const ConstTensorPin recurrentToCellWeightsPin =
2119 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2120 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2121 // [num_units, output_size].
2122 const ConstTensorPin recurrentToOutputWeightsPin =
2123 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2124 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2125 const ConstTensorPin forgetGateBiasPin =
2126 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2127 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2128 const ConstTensorPin cellBiasPin =
2129 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2130 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2131 const ConstTensorPin outputGateBiasPin =
2132 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2133
2134 if (!inputToForgetWeightsPin.IsValid() ||
2135 !inputToCellWeightsPin.IsValid() ||
2136 !inputToOutputWeightsPin.IsValid() ||
2137 !recurrentToForgetWeightsPin.IsValid() ||
2138 !recurrentToCellWeightsPin.IsValid() ||
2139 !recurrentToOutputWeightsPin.IsValid() ||
2140 !forgetGateBiasPin.IsValid() ||
2141 !cellBiasPin.IsValid() ||
2142 !outputGateBiasPin.IsValid())
2143 {
2144 return Fail("%s: Operation has invalid tensor inputs", __func__);
2145 }
2146
2147 // Get the optional input tensors:
2148 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2149 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2150 const ConstTensorPin inputToInputWeightsPin =
2151 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2152 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2153 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2154 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2155 const ConstTensorPin recurrentToInputWeightsPin =
2156 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2157 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2158 const ConstTensorPin cellToInputWeightsPin =
2159 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2160 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2161 const ConstTensorPin cellToForgetWeightsPin =
2162 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2163 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2164 const ConstTensorPin cellToOutputWeightsPin =
2165 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2166 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2167 const ConstTensorPin inputGateBiasPin =
2168 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2169 12,
2170 model,
2171 data,
2172 g_DontPermute,
2173 nullptr,
2174 true);
2175
2176 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2177 // [output_size, num_units].
2178 const ConstTensorPin projectionWeightsPin =
2179 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2180 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2181 const ConstTensorPin projectionBiasPin =
2182 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2183 17,
2184 model,
2185 data,
2186 g_DontPermute,
2187 nullptr,
2188 true);
2189
2190 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2191 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2192 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2193 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2194 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2195 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2196 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2197 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2198 {
2199 return Fail("%s: Operation has invalid tensor inputs", __func__);
2200 }
2201
2202 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2203 // 20: The activation function: A value indicating the activation function:
2204 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2205 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2206 // If set to 0.0 then clipping is disabled.
2207 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2208 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2209 ActivationFn activation;
2210 float cellClip;
2211 float projClip;
2212 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2213 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2214 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2215 {
2216 return Fail("%s: Operation has invalid scalar inputs", __func__);
2217 }
2218
2219 // Get the normalization tensors
2220 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2221 // Used to rescale normalized inputs to activation at input gate.
2222 const ConstTensorPin inputLayerNormWeightsPin
2223 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2224
2225 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2226 // Used to rescale normalized inputs to activation at forget gate.
2227 const ConstTensorPin forgetLayerNormWeightsPin =
2228 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2229 24,
2230 model,
2231 data,
2232 g_DontPermute,
2233 nullptr,
2234 true);
2235
2236 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2237 // Used to rescale normalized inputs to activation at cell gate.
2238 const ConstTensorPin cellLayerNormWeightsPin =
2239 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2240 25,
2241 model,
2242 data,
2243 g_DontPermute,
2244 nullptr,
2245 true);
2246
2247 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2248 // Used to rescale normalized inputs to activation at output gate.
2249 const ConstTensorPin outputLayerNormWeightsPin =
2250 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2251 26,
2252 model,
2253 data,
2254 g_DontPermute,
2255 nullptr,
2256 true);
2257
2258 // Outputs:
2259 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2260 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2261 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2262 if (!scratchBuffer)
2263 {
2264 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2265 }
2266 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2267 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2268 if (!outputStateOut)
2269 {
2270 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2271 }
2272 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2273 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2274 if (!cellStateOut)
2275 {
2276 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2277 }
2278 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2279 // effectively the same as the current “output state (out)” value.
2280 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2281 if (!output)
2282 {
2283 return Fail("%s: Could not read output 3: output", __func__);
2284 }
2285
2286 // set the params structure for the AddLstmLayer call
2287 LstmInputParams params;
2288 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2289 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2290 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2291 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2292 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2293 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2294 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2295 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2296 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2297 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2298 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2299 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2300 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2301 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2302 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2303 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2304 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2305 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2306 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2307 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2308 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2309
2310 // set the layer descriptor
2311 LstmDescriptor desc;
2312 desc.m_ActivationFunc = activation;
2313 desc.m_ClippingThresCell = cellClip;
2314 desc.m_ClippingThresProj = projClip;
2315 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2316 params.m_RecurrentToInputWeights == nullptr ||
2317 params.m_InputGateBias == nullptr);
2318 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2319 params.m_CellToOutputWeights != nullptr);
2320 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2321 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2322 params.m_ForgetLayerNormWeights != nullptr ||
2323 params.m_CellLayerNormWeights != nullptr ||
2324 params.m_OutputLayerNormWeights != nullptr);
2325
2326 // validate the optional input groups
2327 if (desc.m_CifgEnabled &&
2328 (params.m_InputToInputWeights != nullptr ||
2329 params.m_RecurrentToInputWeights != nullptr ||
2330 params.m_InputGateBias != nullptr))
2331 {
2332 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2333 " and input gate bias must be provided", __func__);
2334 }
2335
2336 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2337 {
2338 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2339 }
2340
2341 if (desc.m_PeepholeEnabled &&
2342 (params.m_CellToForgetWeights == nullptr ||
2343 params.m_CellToOutputWeights == nullptr ||
2344 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2345 {
2346 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2347 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2348 }
2349
2350 if (desc.m_LayerNormEnabled &&
2351 (params.m_ForgetLayerNormWeights == nullptr ||
2352 params.m_CellLayerNormWeights == nullptr ||
2353 params.m_OutputLayerNormWeights == nullptr ||
2354 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2355 {
2356 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2357 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2358 }
2359
2360 // Check if the layer is supported
2361 // Inputs
2362 const TensorInfo& inputInfo = input.GetTensorInfo();
2363 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2364 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2365
2366 // Outputs
2367 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2368 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2369 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2370 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2371
2372 // Check if the scratch buffer shape was initialized,
2373 // In some cases the shape could be (0,0) which requires the driver
2374 // to infer the shape and set it up accordingly.
2375 // The code below does that.
2376 TensorInfo fixSbInfo = scratchBufferInfo;
2377 if (IsDynamicTensor(scratchBufferInfo))
2378 {
2379 auto & s = fixSbInfo.GetShape();
2380 s[0] = outputStateInInfo.GetShape()[0];
2381 if (desc.m_CifgEnabled)
2382 {
2383 // 2D tensor with dimensions [num_units * 3, batch_size] with CIFG
2384 s[1] = cellStateOutInfo.GetShape()[1]*3;
2385 }
2386 else
2387 {
2388 // scratch_buffer [num_units * 4, batch_size] without CIFG
2389 s[1] = cellStateOutInfo.GetShape()[1]*4;
2390 }
2391 }
2392
2393 if (IsDynamicTensor(outputStateOutInfo) ||
2394 IsDynamicTensor(cellStateOutInfo) ||
2395 IsDynamicTensor(outputInfo))
2396 {
2397 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2398 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2399 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
2400 }
2401
2402 // Basic parameters
2403 LstmInputParamsInfo paramsInfo;
2404 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2405 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2406 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2407 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2408 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2409 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2410 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2411 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2412 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2413
2414 // Optional parameters
2415 if (!desc.m_CifgEnabled)
2416 {
2417 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2418 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2419 if (params.m_CellToInputWeights != nullptr)
2420 {
2421 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2422 }
2423 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2424 }
2425
2426 if (desc.m_ProjectionEnabled)
2427 {
2428 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2429 if (params.m_ProjectionBias != nullptr)
2430 {
2431 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2432 }
2433 }
2434
2435 if (desc.m_PeepholeEnabled)
2436 {
2437 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2438 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2439 }
2440
2441 if (desc.m_LayerNormEnabled)
2442 {
2443 if(!desc.m_CifgEnabled)
2444 {
2445 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2446 }
2447 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2448 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2449 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2450 }
2451
2452 bool isSupported = false;
2453 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2454 IsLstmSupported,
2455 data.m_Backends,
2456 isSupported,
2457 inputInfo,
2458 outputStateInInfo,
2459 cellStateInInfo,
2460 fixSbInfo,
2461 outputStateOutInfo,
2462 cellStateOutInfo,
2463 outputInfo,
2464 desc,
2465 paramsInfo);
2466 if (!isSupported)
2467 {
2468 return false;
2469 }
2470
2471 // Add the layer
2472 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2473
2474 input.Connect(layer->GetInputSlot(0));
2475 outputStateIn.Connect(layer->GetInputSlot(1));
2476 cellStateIn.Connect(layer->GetInputSlot(2));
2477
2478
2479 return (
2480 (IsDynamicTensor(scratchBufferInfo)?
2481 SetupAndTrackLayerOutputSlotAndOverrideTensorInfo<HalPolicy>(
2482 operation, 0, *layer, 0, model, data,fixSbInfo):
2483 SetupAndTrackLayerOutputSlot<HalPolicy>(
2484 operation, 0, *layer, 0, model, data)) &&
2485 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2486 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2487 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2488}
2489
2490template<typename HalPolicy,
2491 typename HalOperation = typename HalPolicy::Operation,
2492 typename HalModel = typename HalPolicy::Model>
2493bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2494{
2495 using HalOperand = typename HalPolicy::Operand;
2496 using HalOperandType = typename HalPolicy::OperandType;
2497
2498 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2499
2500 if (!input.IsValid())
2501 {
2502 return Fail("%s: Operation has invalid inputs", __func__);
2503 }
2504
2505 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2506
2507 if (!output)
2508 {
2509 return Fail("%s: Could not read output 0", __func__);
2510 }
2511
2512 const TensorInfo& inputInfo = input.GetTensorInfo();
2513 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2514 if (IsDynamicTensor(outputInfo))
2515 {
2516 return Fail("%s: Dynamic output tensors are not supported", __func__);
2517 }
2518
2519 // ArmNN does not currently support non-fixed weights or bias
2520 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2521 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2522
2523 if (weightsOperand == nullptr)
2524 {
2525 return Fail("%s: Operand is invalid", __func__);
2526 }
2527 TransposeConvolution2dDescriptor desc;
2528 desc.m_DataLayout = DataLayout::NHWC;
2529
2530 // Determine whether padding is implicit or explicit
2531 bool implicitPadding = operation.inputs.size() == 9;
2532
2533 if (implicitPadding )
2534 {
2535 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2536 }
2537 else
2538 {
2539 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2540 }
2541
2542 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2543 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2544 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2545
2546 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2547
2548 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2549 // We have to permute it to OIHW if the data layout is NCHW.
2550 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2551 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2552 model, data, OHWIToOIHW) :
2553 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2554
2555 // Bias is a 1D tensor
2556 const ConstTensorPin biasPin =
2557 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2558
2559 if (!weightsPin.IsValid())
2560 {
2561 return Fail("%s: Operation has invalid weights", __func__);
2562 }
2563
2564 if (!biasPin.IsValid())
2565 {
2566 return Fail("%s: Operation has invalid biases", __func__);
2567 }
2568
2569 ConstTensor weights = weightsPin.GetConstTensor();
2570 ConstTensor bias = biasPin.GetConstTensor();
2571 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2572
2573 ActivationFn activation;
2574
2575 if (implicitPadding)
2576 {
2577 int32_t strideX{0};
2578 int32_t strideY{0};
2579 int32_t padLeft{0};
2580 int32_t padRight{0};
2581 int32_t padTop{0};
2582 int32_t padBottom{0};
2583
2584 android::nn::PaddingScheme paddingScheme;
2585 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2586 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2587 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2588 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2589 {
2590 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2591 }
2592
2593 const uint32_t kernelX = weights.GetShape()[widthIndex];
2594 const uint32_t kernelY = weights.GetShape()[heightIndex];
2595 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2596 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
2597
2598 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2599 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
2600
2601 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2602 // but Arm NN only supports values >= 0
2603 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2604 {
2605 return Fail("%s: Negative padding values are not supported", __func__);
2606 }
2607
Colm Donelan8f3d33e2020-07-06 15:41:43 +01002608 // If output shape has been specified as a parameter then extract it and make it available.
2609 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
2610 std::vector<int32_t> outputShape;
2611 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
2612 {
2613 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
2614 for (int dimension : outputShape)
2615 {
2616 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
2617 }
2618 desc.m_OutputShapeEnabled = true;
2619 }
2620
Kevin May42477c12020-03-26 13:34:14 +00002621 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2622 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
2623 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2624 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2625 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2626 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
2627 }
2628 else if (operation.inputs.size() == 11)
2629 {
2630 // explicit padding
2631 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2632 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2633 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2634 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2635 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2636 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
2637 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
2638 {
2639 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2640 }
2641 }
2642 else
2643 {
2644 return Fail("%s: Unsupported number of operation inputs", __func__);
2645 }
2646
2647 desc.m_BiasEnabled = true;
2648 Optional<TensorInfo> biases(bias.GetInfo());
2649
2650 bool isSupported = false;
2651 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2652 IsTransposeConvolution2dSupported,
2653 data.m_Backends,
2654 isSupported,
2655 inputInfo,
2656 outputInfo,
2657 desc,
2658 weights.GetInfo(),
2659 biases);
2660 if (!isSupported)
2661 {
2662 return false;
2663 }
2664
2665 IConnectableLayer* startLayer =
2666 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
2667 if (!startLayer)
2668 {
2669 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2670 }
2671
2672 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2673 if (!endLayer)
2674 {
2675 return Fail("%s: ProcessActivation failed", __func__);
2676 }
2677
2678 input.Connect(startLayer->GetInputSlot(0));
2679
2680 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2681}
2682
2683} // armnn_driver namespace