blob: 29367f2fa4abdfb3ae8859739e7fbeab9918974f [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,
664 typename HalOperation = typename HalPolicy::Operation,
665 typename HalModel = typename HalPolicy::Model>
666bool ConvertGroupedConv2d(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::ConvertGroupedConv2d()");
672
673 //
674 // Parse data
675 //
676 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
677 if (!input.IsValid())
678 {
679 return Fail("%s: Operation has invalid inputs", __func__);
680 }
681 const TensorInfo& inputInfo = input.GetTensorInfo();
682
683 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
684 if (!output)
685 {
686 return Fail("%s: Could not read output 0", __func__);
687 }
688 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
689 if (IsDynamicTensor(outputInfo))
690 {
691 return Fail("%s: Dynamic output tensors are not supported", __func__);
692 }
693
694 // Look ahead to determine data layout
695 DataLayout dataLayout = DataLayout::NHWC;
696 if (operation.inputs.size() == 12)
697 {
698 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
699 }
700 else
701 {
702 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
703 }
704
705 // NOTE:
706 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
707 // but Arm NN expects the filter's height and width indices to match the input's height and
708 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
709 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
710 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
711 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
712 model, data, ohwiToOihw) :
713 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
714 const ConstTensorPin biasesPin =
715 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
716 if (!weightsPin.IsValid() || !biasesPin.IsValid())
717 {
718 return Fail("%s: Operation has invalid inputs", __func__);
719 }
720
721 ConstTensor weights = weightsPin.GetConstTensor();
722 ConstTensor biases = biasesPin.GetConstTensor();
723 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
724
725 const TensorShape& inputShape = inputInfo.GetShape();
726 const TensorShape& outputShape = outputInfo.GetShape();
727 const TensorShape& weightsShape = weights.GetShape();
728 const TensorShape& biasesShape = biases.GetShape();
729
730 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
731 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
732 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
733 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
734
735 Convolution2dDescriptor desc;
736 desc.m_DataLayout = dataLayout;
737 desc.m_BiasEnabled = true;
738
739 int numGroups;
740 ActivationFn activation;
741
742 if (operation.inputs.size() == 12)
743 {
744 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
745 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
746 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
747 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
748 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
749 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
750 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
751 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
752 {
753 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
754 }
755
756 }
757 else if (operation.inputs.size() == 9)
758 {
759 android::nn::PaddingScheme paddingScheme;
760 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
761 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
762 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
763 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
764 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
765 {
766 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
767 }
768
769 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
770 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
771
772 const uint32_t kernelX = weightsShape[widthIndex];
773 const uint32_t kernelY = weightsShape[heightIndex];
774
775 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
776 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
777 }
778 else
779 {
780 return Fail("%s: Unsupported number of operation inputs", __func__);
781 }
782
783 const unsigned int outputChannels = outputShape[channelsIndex];
784
785 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
786 const unsigned int channelMultiplier = outputChannels / numGroups;
787
788 //
789 // Validate all relevant inputs
790 //
791 if (numGroups <= 0)
792 {
793 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
794 }
795
796 if (outputChannels % numGroups != 0u)
797 {
798 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
799 }
800
801 //
802 // Set up Splitter layer
803 //
804 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
805 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
806
807 TensorInfo splitterOutputInfo(4,
808 splitterDimSizes,
809 inputInfo.GetDataType(),
810 inputInfo.GetQuantizationScale(),
811 inputInfo.GetQuantizationOffset());
812
813 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
814
815 ViewsDescriptor splitterDesc(numGroups);
816 for (unsigned int group = 0u; group < numGroups; ++group)
817 {
818 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
819 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
820 {
821 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
822 }
823 }
824
825 bool isSupported = false;
826 FORWARD_LAYER_SUPPORT_FUNC(__func__,
827 IsSplitterSupported,
828 data.m_Backends,
829 isSupported,
830 inputInfo,
831 splitterOutputInfos,
832 splitterDesc);
833 if (!isSupported)
834 {
835 return false;
836 }
837
838 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
839 if (!splitterLayer)
840 {
841 return Fail("%s: Failed to add SplitterLayer", __func__);
842 }
843
844 input.Connect(splitterLayer->GetInputSlot(0));
845 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
846 {
847 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
848 }
849
850 //
851 // Set up Convolution2d layers for each group
852 //
853
854 // Set up group tensor shapes
855 TensorShape groupInputShape(inputShape);
856 groupInputShape[channelsIndex] = channelsPerGroup;
857
858 TensorShape groupOutputShape(outputShape);
859 groupOutputShape[channelsIndex] = 1;
860
861 TensorShape groupWeightsShape(weightsShape);
862 groupWeightsShape[0] /= channelMultiplier * numGroups;
863
864 TensorShape groupBiasesShape({ 1 });
865
866 // Set up group tensor infos
867 TensorInfo groupInputInfo(inputInfo);
868 groupInputInfo.SetShape(groupInputShape);
869
870 const TensorInfo& weightsInfo = weights.GetInfo();
871 TensorInfo groupWeightsInfo(weightsInfo);
872 groupWeightsInfo.SetShape(groupWeightsShape);
873
874 const TensorInfo& biasesInfo = biases.GetInfo();
875 TensorInfo groupBiasesInfo(biasesInfo);
876 groupBiasesInfo.SetShape(groupBiasesShape);
877
878 TensorInfo groupOutputInfo(outputInfo);
879 groupOutputInfo.SetShape(groupOutputShape);
880
881 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
882 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
883
884 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
885 for (unsigned int group = 0u; group < numGroups; ++group)
886 {
887 for (unsigned int m = 0u; m < channelMultiplier; ++m)
888 {
889 auto index = group * channelMultiplier + m;
890
891 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
892 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
893
894 if (weightsInfo.HasPerAxisQuantization())
895 {
896 // Extract per-axis quantization scales for group weights
897 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
898 groupWeightsInfo.SetQuantizationScales(
899 std::vector<float>(weightsQuantScales.begin() + index,
900 weightsQuantScales.begin() + index + groupWeightsShape[0]));
901
902 // Extract per-axis quantization scales for group biases
903 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
904 groupBiasesInfo.SetQuantizationScales(
905 std::vector<float>(biasesQuantScales.begin() + index,
906 biasesQuantScales.begin() + index + groupWeightsShape[0]));
907 }
908
909 // Extract weights and biases data for current group convolution
910 ConstTensor groupWeights(groupWeightsInfo,
911 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
912 weightsDataOffset));
913 ConstTensor groupBiases(groupBiasesInfo,
914 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
915 biasesDataOffset));
916
917 isSupported = false;
918 FORWARD_LAYER_SUPPORT_FUNC(__func__,
919 IsConvolution2dSupported,
920 data.m_Backends,
921 isSupported,
922 groupInputInfo,
923 groupOutputInfo,
924 desc,
925 groupWeightsInfo,
926 Optional<TensorInfo>(groupBiasesInfo));
927 if (!isSupported)
928 {
929 return false;
930 }
931
932 IConnectableLayer* convLayer =
933 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
934 if (!convLayer)
935 {
936 return Fail("%s: AddConvolution2dLayer failed", __func__);
937 }
938
939 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
940 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
941
942 convLayers[index] = convLayer;
943 }
944 }
945
946 //
947 // Set up Concat layer
948 //
949 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
950 for (unsigned int group = 0u; group < numGroups; ++group)
951 {
952 for (unsigned int m = 0u; m < channelMultiplier; ++m)
953 {
954 auto index = group * channelMultiplier + m;
955 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
956 concatDescriptor.SetConcatAxis(channelsIndex);
957 }
958 }
959
960 isSupported = false;
961 FORWARD_LAYER_SUPPORT_FUNC(__func__,
962 IsConcatSupported,
963 data.m_Backends,
964 isSupported,
965 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
966 outputInfo,
967 concatDescriptor);
968 if (!isSupported)
969 {
970 return false;
971 }
972
973 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
974 if (!concatLayer)
975 {
976 return Fail("%s: AddConcatLayer failed", __func__);
977 }
978
979 for (unsigned int group = 0u; group < numGroups; ++group)
980 {
981 for (unsigned int m = 0u; m < channelMultiplier; ++m)
982 {
983 auto index = group * channelMultiplier + m;
984 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
985 }
986 }
987 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
988
989 //
990 // Set up Activation layer (if it is set)
991 //
992 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
993 if (!endLayer)
994 {
995 return Fail("%s: ProcessActivation failed", __func__);
996 }
997
998 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
999}
1000
1001template<typename HalPolicy,
1002 typename HalOperation = typename HalPolicy::Operation,
1003 typename HalModel = typename HalPolicy::Model>
1004bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1005{
1006 using HalOperand = typename HalPolicy::Operand;
1007 using HalOperandType = typename HalPolicy::OperandType;
1008
1009 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1010
1011 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1012 if (!input.IsValid())
1013 {
1014 return Fail("%s: Operation has an invalid input 0", __func__);
1015 }
1016
1017 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1018 if (!output)
1019 {
1020 return Fail("%s: Operation has an invalid output", __func__);
1021 }
1022
1023 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1024 if (IsDynamicTensor(outputInfo))
1025 {
1026 return Fail("%s: Dynamic output tensors are not supported", __func__);
1027 }
1028
1029 // Determine data type of input tensor
1030 HalOperandType inputType;
1031 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1032 {
1033 return Fail("%s: Operation has invalid inputs", __func__);
1034 }
1035
1036 InstanceNormalizationDescriptor desc;
1037
1038 // Read gamma, beta & epsilon
1039 if (inputType == HalOperandType::TENSOR_FLOAT16)
1040 {
1041 Half fp16Gamma;
1042 Half fp16Beta;
1043 Half fp16Epsilon;
1044
1045 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1046 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1047 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1048 {
1049 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1050 }
1051
1052 desc.m_Gamma = static_cast<float>(fp16Gamma);
1053 desc.m_Beta = static_cast<float>(fp16Beta);
1054 desc.m_Eps = static_cast<float>(fp16Epsilon);
1055 }
1056 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1057 {
1058 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1059 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1060 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1061 {
1062 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1063 }
1064 }
1065 else
1066 {
1067 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1068 }
1069
1070 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1071
1072 bool isSupported = false;
1073 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1074 IsInstanceNormalizationSupported,
1075 data.m_Backends,
1076 isSupported,
1077 input.GetTensorInfo(),
1078 outputInfo,
1079 desc);
1080 if (!isSupported)
1081 {
1082 return false;
1083 }
1084
1085 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1086 input.Connect(layer->GetInputSlot(0));
1087
1088 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1089}
1090
1091template<typename HalPolicy,
1092 typename HalOperation = typename HalPolicy::Operation,
1093 typename HalModel = typename HalPolicy::Model>
1094bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1095{
1096 using HalOperand = typename HalPolicy::Operand;
1097 using HalOperandType = typename HalPolicy::OperandType;
1098
1099 ALOGV("HalPolicy::ConvertLogSoftmax()");
1100
1101 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1102 if (!input.IsValid())
1103 {
1104 return Fail("%s: Failed to read input 0", __func__);
1105 }
1106
1107 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1108 if (!output)
1109 {
1110 return Fail("%s: Failed to read output", __func__);
1111 }
1112
1113 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1114 if (IsDynamicTensor(outputInfo))
1115 {
1116 return Fail("%s: Dynamic output tensors are not supported", __func__);
1117 }
1118
1119 // Determine data type of input tensor
1120 HalOperandType inputType;
1121 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1122 {
1123 return Fail("%s: Operation has invalid inputs", __func__);
1124 }
1125
1126 LogSoftmaxDescriptor descriptor;
1127
1128 // Read beta
1129 if (inputType == HalOperandType::TENSOR_FLOAT16)
1130 {
1131 Half fp16Beta;
1132 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1133 {
1134 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1135 }
1136
1137 descriptor.m_Beta = static_cast<float>(fp16Beta);
1138 }
1139 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1140 {
1141 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1142 {
1143 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1144 }
1145 }
1146 else
1147 {
1148 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1149 }
1150
1151 // Read axis
1152 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1153 {
1154 return Fail("%s: Failed to read input 2", __func__);
1155 }
1156
1157 bool isSupported = false;
1158 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1159 IsLogSoftmaxSupported,
1160 data.m_Backends,
1161 isSupported,
1162 input.GetTensorInfo(),
1163 outputInfo,
1164 descriptor);
1165 if (!isSupported)
1166 {
1167 return false;
1168 }
1169
1170 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
1171 if (!layer)
1172 {
1173 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1174 }
1175
1176 input.Connect(layer->GetInputSlot(0));
1177
1178 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1179}
1180
1181template<typename HalPolicy,
1182 typename HalOperation = typename HalPolicy::Operation,
1183 typename HalModel = typename HalPolicy::Model>
1184bool ConvertMaximum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1185{
1186 using HalOperand = typename HalPolicy::Operand;
1187
1188 ALOGV("HalPolicy::ConvertMaximum()");
1189
1190 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1191 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1192
1193 if (!input0.IsValid() || !input1.IsValid())
1194 {
1195 return Fail("%s: Operation has invalid inputs", __func__);
1196 }
1197
1198 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1199 if (!outputOperand)
1200 {
1201 return Fail("%s: Could not read output", __func__);
1202 }
1203
1204 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
1205 if (IsDynamicTensor(outInfo))
1206 {
1207 return Fail("%s: Dynamic output tensors are not supported", __func__);
1208 }
1209
1210 bool isSupported = false;
1211 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1212 IsMaximumSupported,
1213 data.m_Backends,
1214 isSupported,
1215 input0.GetTensorInfo(),
1216 input1.GetTensorInfo(),
1217 outInfo);
1218
1219 if (!isSupported)
1220 {
1221 return false;
1222 }
1223
1224 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
1225 assert(layer != nullptr);
1226 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1227 if (!isReshapeSupported)
1228 {
1229 return false;
1230 }
1231
1232 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1233}
1234
1235template<typename HalPolicy,
1236 typename HalOperation = typename HalPolicy::Operation,
1237 typename HalModel = typename HalPolicy::Model>
1238bool ConvertMinimum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1239{
1240 using HalOperand = typename HalPolicy::Operand;
1241
1242 ALOGV("HalPolicy::ConvertMinimum()");
1243
1244 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1245 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1246
1247 if (!input0.IsValid() || !input1.IsValid())
1248 {
1249 return Fail("%s: Operation has invalid inputs", __func__);
1250 }
1251
1252 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1253 if (!output)
1254 {
1255 return Fail("%s: Could not read output 0", __func__);
1256 }
1257
1258 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1259 if (IsDynamicTensor(outputInfo))
1260 {
1261 return Fail("%s: Dynamic output tensors are not supported", __func__);
1262 }
1263
1264 bool isSupported = false;
1265 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1266 IsMinimumSupported,
1267 data.m_Backends,
1268 isSupported,
1269 input0.GetTensorInfo(),
1270 input1.GetTensorInfo(),
1271 outputInfo);
1272
1273 if (!isSupported)
1274 {
1275 return false;
1276 }
1277
1278 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
1279 assert(layer != nullptr);
1280 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1281 if (!isReshapeSupported)
1282 {
1283 return false;
1284 }
1285
1286 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1287}
1288
1289template<typename HalPolicy,
1290 typename HalOperation = typename HalPolicy::Operation,
1291 typename HalModel = typename HalPolicy::Model>
1292bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1293{
1294 using HalOperand = typename HalPolicy::Operand;
1295 using HalOperandType = typename HalPolicy::OperandType;
1296
1297 ALOGV("HalPolicy::ConvertPadV2()");
1298
1299 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1300 if (!input.IsValid())
1301 {
1302 return Fail("%s: Could not read input 0", __func__);
1303 }
1304
1305 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1306 if (!output)
1307 {
1308 return Fail("%s: Could not read output", __func__);
1309 }
1310
1311 const TensorInfo& inputInfo = input.GetTensorInfo();
1312 unsigned int rank = inputInfo.GetNumDimensions();
1313
1314 PadDescriptor descriptor;
1315 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1316 {
1317 return Fail("%s: Could not convert paddings", __func__);
1318 }
1319
1320 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1321 if (IsDynamicTensor(outputInfo))
1322 {
1323 return Fail("%s: Dynamic output tensors are not supported", __func__);
1324 }
1325
1326 // Determine type of padding value
1327 HalOperandType operandType0;
1328 HalOperandType operandType2;
1329
1330 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1331 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1332 {
1333 return Fail("%s: Operation has invalid inputs", __func__);
1334 }
1335
1336 // Read value to use for padding
1337 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1338 {
1339 Half f16PadValue;
1340 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1341 {
1342 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1343 }
1344
1345 descriptor.m_PadValue = f16PadValue;
1346 }
1347 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1348 {
1349 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1350 {
1351 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1352 }
1353 }
1354 else if (operandType0 == HalOperandType::TENSOR_QUANT8_ASYMM && operandType2 == HalOperandType::INT32)
1355 {
1356 int32_t intPadValue = 0;
1357 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1358 {
1359 return Fail("%s: Could not read input 2 (INT32)", __func__);
1360 }
1361 descriptor.m_PadValue = intPadValue;
1362 }
1363 else
1364 {
1365 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1366 }
1367
1368 bool isSupported = false;
1369 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1370 IsPadSupported,
1371 data.m_Backends,
1372 isSupported,
1373 inputInfo,
1374 outputInfo,
1375 descriptor);
1376 if (!isSupported)
1377 {
1378 return false;
1379 }
1380
1381 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
1382 assert(layer != nullptr);
1383 input.Connect(layer->GetInputSlot(0));
1384 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1385
1386 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1387}
1388
1389template<typename HalPolicy,
1390 typename HalOperation = typename HalPolicy::Operation,
1391 typename HalModel = typename HalPolicy::Model>
1392bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1393{
1394 using HalOperand = typename HalPolicy::Operand;
1395
1396 ALOGV("HalPolicy::ConvertPrelu()");
1397
1398 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1399 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1400
1401 if (!input.IsValid() || !alpha.IsValid())
1402 {
1403 return Fail("%s: Operation has invalid inputs", __func__);
1404 }
1405
1406 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1407
1408 if (!output)
1409 {
1410 return Fail("%s: Could not read output", __func__);
1411 }
1412
1413 const TensorInfo& inputInfo = input.GetTensorInfo();
1414 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1415 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1416
1417 if (IsDynamicTensor(outputInfo))
1418 {
1419 return Fail("%s: Dynamic output tensors are not supported", __func__);
1420 }
1421
1422 bool isSupported = false;
1423 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1424 IsPreluSupported,
1425 data.m_Backends,
1426 isSupported,
1427 inputInfo,
1428 alphaInfo,
1429 outputInfo);
1430 if (!isSupported)
1431 {
1432 return false;
1433 }
1434
1435 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
1436
1437 if (!layer)
1438 {
1439 return Fail("%s: AddPreluLayer failed", __func__);
1440 }
1441
1442 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1443 if (!isReshapeSupported)
1444 {
1445 return false;
1446 }
1447
1448 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1449}
1450
1451template<typename HalPolicy,
1452 typename HalOperation = typename HalPolicy::Operation,
1453 typename HalModel = typename HalPolicy::Model>
1454bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1455{
1456 using HalOperand = typename HalPolicy::Operand;
1457
1458 ALOGV("HalPolicy::ConvertQuantize()");
1459
1460 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1461 if (!input.IsValid())
1462 {
1463 return Fail("%s: Operation has invalid input", __func__);
1464 }
1465
1466 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1467 if (!outputOperand)
1468 {
1469 return Fail("%s: Operation has invalid outputs", __func__);
1470 }
1471
1472 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
1473 if (IsDynamicTensor(outputInfo))
1474 {
1475 return Fail("%s: Dynamic output tensors are not supported", __func__);
1476 }
1477
1478 bool isSupported = false;
1479 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1480 IsQuantizeSupported,
1481 data.m_Backends,
1482 isSupported,
1483 input.GetTensorInfo(),
1484 outputInfo);
1485 if (!isSupported)
1486 {
1487 return false;
1488 }
1489
1490 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
1491 assert(layer != nullptr);
1492 input.Connect(layer->GetInputSlot(0));
1493
1494 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1495}
1496
1497template<typename HalPolicy,
1498 typename HalOperation = typename HalPolicy::Operation,
1499 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001500bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001501{
1502 using HalOperand = typename HalPolicy::Operand;
1503
Sadik Armagan813f2302020-05-19 14:10:30 +01001504 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001505
1506 //Inputs:
1507 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1508 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1509 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1510 if (!input.IsValid())
1511 {
1512 return Fail("%s: Could not read input 0: input", __func__);
1513 }
1514
1515 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1516 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1517 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1518 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1519 if (!previousCellStateIn.IsValid())
1520 {
1521 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1522 }
1523
1524 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1525 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1526 // is quantized with a fixed quantization range of -1, 127/128.
1527 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1528 if (!previousOutputIn.IsValid())
1529 {
1530 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1531 }
1532
1533 // Get the input tensors:
1534 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1535 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1536 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1537 const ConstTensorPin inputToInputWeightsPin =
1538 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1539
1540 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1541 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1542 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1543 const ConstTensorPin inputToForgetWeightsPin =
1544 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1545
1546 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1547 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1548 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1549 const ConstTensorPin inputToCellWeightsPin =
1550 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1551
1552 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1553 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1554 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1555 const ConstTensorPin inputToOutputWeightsPin =
1556 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1557
1558 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1559 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1560 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1561 const ConstTensorPin recurrentToInputWeightsPin =
1562 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1563
1564 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1565 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1566 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1567 const ConstTensorPin recurrentToForgetWeightsPin =
1568 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1569
1570 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1571 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1572 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1573 const ConstTensorPin recurrentToCellWeightsPin =
1574 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1575
1576 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1577 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1578 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1579 const ConstTensorPin recurrentToOutputWeightsPin =
1580 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1581
1582 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1583 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1584 // of input and weights scales and zeroPoint equal to 0.
1585 const ConstTensorPin inputGateBiasPin =
1586 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1587
1588 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1589 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1590 // of input and weights scales and zeroPoint equal to 0.
1591 const ConstTensorPin forgetGateBiasPin =
1592 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1593
1594 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1595 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1596 // and weights scales and zeroPoint equal to 0.
1597 const ConstTensorPin cellBiasPin =
1598 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1599
1600 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1601 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1602 // of input and weights scales and zeroPoint equal to 0.
1603 const ConstTensorPin outputGateBiasPin =
1604 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1605
1606 if (!inputToInputWeightsPin.IsValid() ||
1607 !inputToForgetWeightsPin.IsValid() ||
1608 !inputToCellWeightsPin.IsValid() ||
1609 !inputToOutputWeightsPin.IsValid() ||
1610 !recurrentToInputWeightsPin.IsValid() ||
1611 !recurrentToForgetWeightsPin.IsValid() ||
1612 !recurrentToCellWeightsPin.IsValid() ||
1613 !recurrentToOutputWeightsPin.IsValid() ||
1614 !inputGateBiasPin.IsValid() ||
1615 !forgetGateBiasPin.IsValid() ||
1616 !cellBiasPin.IsValid() ||
1617 !outputGateBiasPin.IsValid())
1618 {
1619 return Fail("%s: Operation has invalid tensor inputs", __func__);
1620 }
1621
1622 // Outputs:
1623 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1624 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1625 // of -2^4, 2^4 * 32767/32768.
1626 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1627 if (!cellStateOut)
1628 {
1629 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1630 }
1631
1632 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1633 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1634 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1635 if (!output)
1636 {
1637 return Fail("%s: Could not read output 1: output", __func__);
1638 }
1639
1640 // Inputs
1641 const TensorInfo& inputInfo = input.GetTensorInfo();
1642 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1643 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1644
1645 // Outputs
1646 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1647 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1648
1649 // Dynamic tensors currently not supported
1650 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1651 {
1652 return Fail("%s: Dynamic output tensors are not supported", __func__);
1653 }
1654
1655 QuantizedLstmInputParams params;
1656
1657 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1658 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1659 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1660 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1661 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1662 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1663 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1664 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1665 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1666 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1667 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1668 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1669
1670 QuantizedLstmInputParamsInfo paramsInfo;
1671 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1672 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1673 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1674 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1675 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1676 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1677 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1678 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1679 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1680 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1681 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1682 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1683
1684 bool isSupported = false;
1685 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1686 IsQuantizedLstmSupported,
1687 data.m_Backends,
1688 isSupported,
1689 inputInfo,
1690 previousCellStateInInfo,
1691 previousOutputInInfo,
1692 cellStateOutInfo,
1693 outputInfo,
1694 paramsInfo);
1695
1696 if (!isSupported)
1697 {
1698 return false;
1699 }
1700
1701 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1702 input.Connect(layer->GetInputSlot(0));
1703 previousCellStateIn.Connect(layer->GetInputSlot(1));
1704 previousOutputIn.Connect(layer->GetInputSlot(2));
1705
1706 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
1707 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
1708}
1709
1710template<typename HalPolicy,
1711 typename HalOperation = typename HalPolicy::Operation,
1712 typename HalModel = typename HalPolicy::Model>
1713bool ConvertResize(const HalOperation& operation,
1714 const HalModel& model,
1715 ConversionData& data,
1716 ResizeMethod resizeMethod)
1717{
1718 using HalOperand = typename HalPolicy::Operand;
1719 using HalOperandType = typename HalPolicy::OperandType;
1720 ALOGV("HalPolicy::ConvertResize()");
1721 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
1722
1723 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1724 if (!input.IsValid())
1725 {
1726 return Fail("%s: Could not read input 0", __func__);
1727 }
1728
1729 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1730 if (!output)
1731 {
1732 return Fail("%s: Could not read output 0", __func__);
1733 }
1734
1735 const TensorInfo& inputInfo = input.GetTensorInfo();
1736 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1737
1738 if (IsDynamicTensor(outputInfo))
1739 {
1740 return Fail("%s: Dynamic output tensors are not supported", __func__);
1741 }
1742
1743 ResizeDescriptor descriptor;
1744 descriptor.m_Method = resizeMethod;
1745 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
1746
1747 HalOperandType operandType1;
1748 HalOperandType operandType2;
1749
1750 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
1751 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1752 {
1753 return Fail("%s: Operation has invalid inputs", __func__);
1754 }
1755
1756 if (operandType1 != operandType2)
1757 {
1758 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1759 }
1760
1761 if (operandType1 == HalOperandType::INT32)
1762 {
1763 // Case 1: resizing by shape
1764 int32_t targetWidth = 0;
1765 int32_t targetHeight = 0;
1766
1767 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
1768 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
1769 {
1770 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1771 }
1772
1773 if (targetWidth < 0 || targetHeight < 0)
1774 {
1775 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1776 "Target width/height cannot be < 0", __func__);
1777 }
1778
1779 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
1780 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
1781 }
1782 else if (operandType1 == HalOperandType::FLOAT32)
1783 {
1784 // Case 2: resizing by scale
1785 float widthScale = 1.0f;
1786 float heightScale = 1.0f;
1787
1788 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
1789 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
1790 {
1791 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1792 }
1793
1794 const TensorShape& inputShape = inputInfo.GetShape();
1795 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1796
1797 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1798 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1799
1800 descriptor.m_TargetWidth = std::floor(width * widthScale);
1801 descriptor.m_TargetHeight = std::floor(height * heightScale);
1802 }
1803 else if (operandType1 == HalOperandType::FLOAT16)
1804 {
1805 Half widthScale;
1806 Half heightScale;
1807
1808 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
1809 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
1810 {
1811 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1812 }
1813
1814 const TensorShape& inputShape = inputInfo.GetShape();
1815 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1816
1817 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
1818 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
1819
1820 descriptor.m_TargetWidth = std::floor(width * widthScale);
1821 descriptor.m_TargetHeight = std::floor(height * heightScale);
1822 }
1823 else
1824 {
1825 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
1826 }
1827
David Monahan51e0b132020-04-20 16:12:06 +01001828 descriptor.m_BilinearAlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
1829 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
1830 if (descriptor.m_BilinearAlignCorners)
1831 {
1832 return Fail("%s: Resize Align Corners is not currently supported", __func__);
1833 }
1834 if (descriptor.m_HalfPixelCenters)
1835 {
1836 return Fail("%s: Resize Half Pixel Centers is not currently supported", __func__);
1837 }
1838
Kevin May42477c12020-03-26 13:34:14 +00001839 bool isSupported = false;
1840 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1841 IsResizeSupported,
1842 data.m_Backends,
1843 isSupported,
1844 inputInfo,
1845 outputInfo,
1846 descriptor);
1847
1848 if (!isSupported)
1849 {
1850 return false;
1851 }
1852
1853 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
1854
1855 assert(layer != nullptr);
1856
1857 input.Connect(layer->GetInputSlot(0));
1858
1859 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1860}
1861
1862template<typename HalPolicy,
1863 typename HalOperation = typename HalPolicy::Operation,
1864 typename HalModel = typename HalPolicy::Model>
1865bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
1866{
1867 using HalOperand = typename HalPolicy::Operand;
1868 using HalOperandType = typename HalPolicy::OperandType;
1869
1870 ALOGV("HalPolicy::ConvertSpaceToDepth()");
1871
1872 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1873 if (!input.IsValid() )
1874 {
1875 return Fail("%s: Operation has invalid inputs", __func__);
1876 }
1877
1878 const TensorInfo& inputInfo = input.GetTensorInfo();
1879 unsigned int rank = inputInfo.GetNumDimensions();
1880 if (rank != 4)
1881 {
1882 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1883 }
1884
1885 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1886 if (!output)
1887 {
1888 return Fail("%s: Could not read output 0", __func__);
1889 }
1890
1891 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1892 if (IsDynamicTensor(outputInfo))
1893 {
1894 return Fail("%s: Dynamic output tensors are not supported", __func__);
1895 }
1896
1897 SpaceToDepthDescriptor desc;
1898
1899 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
1900
1901 if (desc.m_BlockSize <= 1)
1902 {
1903 return Fail("%s: Block size must be at least 1 in all dimensions");
1904 }
1905
1906 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
1907
1908 bool isSupported = false;
1909 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1910 IsSpaceToDepthSupported,
1911 data.m_Backends,
1912 isSupported,
1913 inputInfo,
1914 outputInfo,
1915 desc);
1916 if (!isSupported)
1917 {
1918 return false;
1919 }
1920
1921 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
1922 assert(layer != nullptr);
1923 input.Connect(layer->GetInputSlot(0));
1924
1925 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1926}
1927
1928template<typename HalPolicy,
1929 typename HalOperation = typename HalPolicy::Operation,
1930 typename HalModel = typename HalPolicy::Model>
1931bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1932{
1933 using HalOperand = typename HalPolicy::Operand;
1934 using HalOperandType = typename HalPolicy::OperandType;
1935
1936 ALOGV("HalPolicy::ConvertSoftmax()");
1937
1938 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1939 if (!input.IsValid())
1940 {
1941 return Fail("%s: Operation has invalid inputs", __func__);
1942 }
1943
1944 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1945 if (!outputOperand)
1946 {
1947 return Fail("%s: Operation has no outputs", __func__);
1948 }
1949
1950 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
1951 if (IsDynamicTensor(outputInfo))
1952 {
1953 return Fail("%s: Dynamic output tensors are not supported", __func__);
1954 }
1955
1956 SoftmaxDescriptor desc;
1957 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
1958 {
1959 return Fail("%s: Operation has invalid inputs", __func__);
1960 }
1961
1962 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
1963 2,
1964 HalOperandType::INT32,
1965 desc.m_Axis,
1966 model,
1967 data))
1968 {
1969 return Fail("%s: Operation has invalid inputs", __func__);
1970 }
1971
1972 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1973 !(desc.m_Axis == 1 ||
1974 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1975 {
1976 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1977 }
1978
1979 bool isSupported = false;
1980 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1981 IsSoftmaxSupported,
1982 data.m_Backends,
1983 isSupported,
1984 input.GetTensorInfo(),
1985 outputInfo,
1986 desc);
1987 if (!isSupported)
1988 {
1989 return false;
1990 }
1991
1992 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1993 assert(layer != nullptr);
1994 input.Connect(layer->GetInputSlot(0));
1995
1996 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1997}
1998
1999template<typename HalPolicy,
2000 typename HalOperation = typename HalPolicy::Operation,
2001 typename HalModel = typename HalPolicy::Model>
2002bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2003{
2004 using HalOperand = typename HalPolicy::Operand;
2005 using HalOperandType = typename HalPolicy::OperandType;
2006
2007 ALOGV("HalPolicy::ConvertLstm()");
2008
2009 // Inputs:
2010 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2011 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2012 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2013 if (!input.IsValid())
2014 {
2015 return Fail("%s: Could not read input 0: input", __func__);
2016 }
2017 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2018 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2019 if (!outputStateIn.IsValid())
2020 {
2021 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2022 }
2023 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2024 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2025 if (!cellStateIn.IsValid())
2026 {
2027 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2028 }
2029
2030 // Get the mandatory input tensors:
2031 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2032 // [num_units, input_size].
2033 const ConstTensorPin inputToForgetWeightsPin =
2034 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2035 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2036 // [num_units, input_size].
2037 const ConstTensorPin inputToCellWeightsPin =
2038 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2039 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2040 // [num_units, input_size].
2041 const ConstTensorPin inputToOutputWeightsPin =
2042 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2043 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2044 // [num_units, output_size].
2045 const ConstTensorPin recurrentToForgetWeightsPin =
2046 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2047 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2048 // [num_units, output_size].
2049 const ConstTensorPin recurrentToCellWeightsPin =
2050 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2051 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2052 // [num_units, output_size].
2053 const ConstTensorPin recurrentToOutputWeightsPin =
2054 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2055 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2056 const ConstTensorPin forgetGateBiasPin =
2057 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2058 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2059 const ConstTensorPin cellBiasPin =
2060 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2061 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2062 const ConstTensorPin outputGateBiasPin =
2063 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2064
2065 if (!inputToForgetWeightsPin.IsValid() ||
2066 !inputToCellWeightsPin.IsValid() ||
2067 !inputToOutputWeightsPin.IsValid() ||
2068 !recurrentToForgetWeightsPin.IsValid() ||
2069 !recurrentToCellWeightsPin.IsValid() ||
2070 !recurrentToOutputWeightsPin.IsValid() ||
2071 !forgetGateBiasPin.IsValid() ||
2072 !cellBiasPin.IsValid() ||
2073 !outputGateBiasPin.IsValid())
2074 {
2075 return Fail("%s: Operation has invalid tensor inputs", __func__);
2076 }
2077
2078 // Get the optional input tensors:
2079 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2080 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2081 const ConstTensorPin inputToInputWeightsPin =
2082 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2083 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2084 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2085 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2086 const ConstTensorPin recurrentToInputWeightsPin =
2087 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2088 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2089 const ConstTensorPin cellToInputWeightsPin =
2090 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2091 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2092 const ConstTensorPin cellToForgetWeightsPin =
2093 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2094 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2095 const ConstTensorPin cellToOutputWeightsPin =
2096 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2097 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2098 const ConstTensorPin inputGateBiasPin =
2099 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2100 12,
2101 model,
2102 data,
2103 g_DontPermute,
2104 nullptr,
2105 true);
2106
2107 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2108 // [output_size, num_units].
2109 const ConstTensorPin projectionWeightsPin =
2110 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2111 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2112 const ConstTensorPin projectionBiasPin =
2113 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2114 17,
2115 model,
2116 data,
2117 g_DontPermute,
2118 nullptr,
2119 true);
2120
2121 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2122 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2123 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2124 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2125 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2126 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2127 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2128 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2129 {
2130 return Fail("%s: Operation has invalid tensor inputs", __func__);
2131 }
2132
2133 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2134 // 20: The activation function: A value indicating the activation function:
2135 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2136 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2137 // If set to 0.0 then clipping is disabled.
2138 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2139 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2140 ActivationFn activation;
2141 float cellClip;
2142 float projClip;
2143 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2144 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2145 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2146 {
2147 return Fail("%s: Operation has invalid scalar inputs", __func__);
2148 }
2149
2150 // Get the normalization tensors
2151 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2152 // Used to rescale normalized inputs to activation at input gate.
2153 const ConstTensorPin inputLayerNormWeightsPin
2154 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2155
2156 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2157 // Used to rescale normalized inputs to activation at forget gate.
2158 const ConstTensorPin forgetLayerNormWeightsPin =
2159 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2160 24,
2161 model,
2162 data,
2163 g_DontPermute,
2164 nullptr,
2165 true);
2166
2167 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2168 // Used to rescale normalized inputs to activation at cell gate.
2169 const ConstTensorPin cellLayerNormWeightsPin =
2170 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2171 25,
2172 model,
2173 data,
2174 g_DontPermute,
2175 nullptr,
2176 true);
2177
2178 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2179 // Used to rescale normalized inputs to activation at output gate.
2180 const ConstTensorPin outputLayerNormWeightsPin =
2181 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2182 26,
2183 model,
2184 data,
2185 g_DontPermute,
2186 nullptr,
2187 true);
2188
2189 // Outputs:
2190 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2191 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2192 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2193 if (!scratchBuffer)
2194 {
2195 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2196 }
2197 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2198 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2199 if (!outputStateOut)
2200 {
2201 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2202 }
2203 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2204 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2205 if (!cellStateOut)
2206 {
2207 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2208 }
2209 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2210 // effectively the same as the current “output state (out)” value.
2211 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2212 if (!output)
2213 {
2214 return Fail("%s: Could not read output 3: output", __func__);
2215 }
2216
2217 // set the params structure for the AddLstmLayer call
2218 LstmInputParams params;
2219 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2220 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2221 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2222 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2223 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2224 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2225 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2226 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2227 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2228 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2229 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2230 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2231 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2232 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2233 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2234 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2235 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2236 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2237 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2238 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2239 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2240
2241 // set the layer descriptor
2242 LstmDescriptor desc;
2243 desc.m_ActivationFunc = activation;
2244 desc.m_ClippingThresCell = cellClip;
2245 desc.m_ClippingThresProj = projClip;
2246 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2247 params.m_RecurrentToInputWeights == nullptr ||
2248 params.m_InputGateBias == nullptr);
2249 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2250 params.m_CellToOutputWeights != nullptr);
2251 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2252 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2253 params.m_ForgetLayerNormWeights != nullptr ||
2254 params.m_CellLayerNormWeights != nullptr ||
2255 params.m_OutputLayerNormWeights != nullptr);
2256
2257 // validate the optional input groups
2258 if (desc.m_CifgEnabled &&
2259 (params.m_InputToInputWeights != nullptr ||
2260 params.m_RecurrentToInputWeights != nullptr ||
2261 params.m_InputGateBias != nullptr))
2262 {
2263 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2264 " and input gate bias must be provided", __func__);
2265 }
2266
2267 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2268 {
2269 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2270 }
2271
2272 if (desc.m_PeepholeEnabled &&
2273 (params.m_CellToForgetWeights == nullptr ||
2274 params.m_CellToOutputWeights == nullptr ||
2275 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2276 {
2277 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2278 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2279 }
2280
2281 if (desc.m_LayerNormEnabled &&
2282 (params.m_ForgetLayerNormWeights == nullptr ||
2283 params.m_CellLayerNormWeights == nullptr ||
2284 params.m_OutputLayerNormWeights == nullptr ||
2285 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2286 {
2287 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2288 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2289 }
2290
2291 // Check if the layer is supported
2292 // Inputs
2293 const TensorInfo& inputInfo = input.GetTensorInfo();
2294 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2295 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2296
2297 // Outputs
2298 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2299 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2300 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2301 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2302
2303 // Check if the scratch buffer shape was initialized,
2304 // In some cases the shape could be (0,0) which requires the driver
2305 // to infer the shape and set it up accordingly.
2306 // The code below does that.
2307 TensorInfo fixSbInfo = scratchBufferInfo;
2308 if (IsDynamicTensor(scratchBufferInfo))
2309 {
2310 auto & s = fixSbInfo.GetShape();
2311 s[0] = outputStateInInfo.GetShape()[0];
2312 if (desc.m_CifgEnabled)
2313 {
2314 // 2D tensor with dimensions [num_units * 3, batch_size] with CIFG
2315 s[1] = cellStateOutInfo.GetShape()[1]*3;
2316 }
2317 else
2318 {
2319 // scratch_buffer [num_units * 4, batch_size] without CIFG
2320 s[1] = cellStateOutInfo.GetShape()[1]*4;
2321 }
2322 }
2323
2324 if (IsDynamicTensor(outputStateOutInfo) ||
2325 IsDynamicTensor(cellStateOutInfo) ||
2326 IsDynamicTensor(outputInfo))
2327 {
2328 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2329 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2330 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
2331 }
2332
2333 // Basic parameters
2334 LstmInputParamsInfo paramsInfo;
2335 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2336 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2337 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2338 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2339 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2340 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2341 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2342 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2343 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2344
2345 // Optional parameters
2346 if (!desc.m_CifgEnabled)
2347 {
2348 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2349 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2350 if (params.m_CellToInputWeights != nullptr)
2351 {
2352 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2353 }
2354 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2355 }
2356
2357 if (desc.m_ProjectionEnabled)
2358 {
2359 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2360 if (params.m_ProjectionBias != nullptr)
2361 {
2362 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2363 }
2364 }
2365
2366 if (desc.m_PeepholeEnabled)
2367 {
2368 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2369 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2370 }
2371
2372 if (desc.m_LayerNormEnabled)
2373 {
2374 if(!desc.m_CifgEnabled)
2375 {
2376 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2377 }
2378 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2379 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2380 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2381 }
2382
2383 bool isSupported = false;
2384 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2385 IsLstmSupported,
2386 data.m_Backends,
2387 isSupported,
2388 inputInfo,
2389 outputStateInInfo,
2390 cellStateInInfo,
2391 fixSbInfo,
2392 outputStateOutInfo,
2393 cellStateOutInfo,
2394 outputInfo,
2395 desc,
2396 paramsInfo);
2397 if (!isSupported)
2398 {
2399 return false;
2400 }
2401
2402 // Add the layer
2403 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2404
2405 input.Connect(layer->GetInputSlot(0));
2406 outputStateIn.Connect(layer->GetInputSlot(1));
2407 cellStateIn.Connect(layer->GetInputSlot(2));
2408
2409
2410 return (
2411 (IsDynamicTensor(scratchBufferInfo)?
2412 SetupAndTrackLayerOutputSlotAndOverrideTensorInfo<HalPolicy>(
2413 operation, 0, *layer, 0, model, data,fixSbInfo):
2414 SetupAndTrackLayerOutputSlot<HalPolicy>(
2415 operation, 0, *layer, 0, model, data)) &&
2416 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2417 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2418 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2419}
2420
2421template<typename HalPolicy,
2422 typename HalOperation = typename HalPolicy::Operation,
2423 typename HalModel = typename HalPolicy::Model>
2424bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2425{
2426 using HalOperand = typename HalPolicy::Operand;
2427 using HalOperandType = typename HalPolicy::OperandType;
2428
2429 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2430
2431 if (!input.IsValid())
2432 {
2433 return Fail("%s: Operation has invalid inputs", __func__);
2434 }
2435
2436 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2437
2438 if (!output)
2439 {
2440 return Fail("%s: Could not read output 0", __func__);
2441 }
2442
2443 const TensorInfo& inputInfo = input.GetTensorInfo();
2444 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2445 if (IsDynamicTensor(outputInfo))
2446 {
2447 return Fail("%s: Dynamic output tensors are not supported", __func__);
2448 }
2449
2450 // ArmNN does not currently support non-fixed weights or bias
2451 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2452 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2453
2454 if (weightsOperand == nullptr)
2455 {
2456 return Fail("%s: Operand is invalid", __func__);
2457 }
2458 TransposeConvolution2dDescriptor desc;
2459 desc.m_DataLayout = DataLayout::NHWC;
2460
2461 // Determine whether padding is implicit or explicit
2462 bool implicitPadding = operation.inputs.size() == 9;
2463
2464 if (implicitPadding )
2465 {
2466 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2467 }
2468 else
2469 {
2470 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2471 }
2472
2473 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2474 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2475 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2476
2477 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2478
2479 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2480 // We have to permute it to OIHW if the data layout is NCHW.
2481 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2482 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2483 model, data, OHWIToOIHW) :
2484 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2485
2486 // Bias is a 1D tensor
2487 const ConstTensorPin biasPin =
2488 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2489
2490 if (!weightsPin.IsValid())
2491 {
2492 return Fail("%s: Operation has invalid weights", __func__);
2493 }
2494
2495 if (!biasPin.IsValid())
2496 {
2497 return Fail("%s: Operation has invalid biases", __func__);
2498 }
2499
2500 ConstTensor weights = weightsPin.GetConstTensor();
2501 ConstTensor bias = biasPin.GetConstTensor();
2502 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2503
2504 ActivationFn activation;
2505
2506 if (implicitPadding)
2507 {
2508 int32_t strideX{0};
2509 int32_t strideY{0};
2510 int32_t padLeft{0};
2511 int32_t padRight{0};
2512 int32_t padTop{0};
2513 int32_t padBottom{0};
2514
2515 android::nn::PaddingScheme paddingScheme;
2516 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2517 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2518 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2519 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2520 {
2521 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2522 }
2523
2524 const uint32_t kernelX = weights.GetShape()[widthIndex];
2525 const uint32_t kernelY = weights.GetShape()[heightIndex];
2526 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2527 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
2528
2529 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2530 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
2531
2532 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2533 // but Arm NN only supports values >= 0
2534 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2535 {
2536 return Fail("%s: Negative padding values are not supported", __func__);
2537 }
2538
2539 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2540 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
2541 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2542 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2543 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2544 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
2545 }
2546 else if (operation.inputs.size() == 11)
2547 {
2548 // explicit padding
2549 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2550 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2551 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2552 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2553 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2554 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
2555 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
2556 {
2557 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2558 }
2559 }
2560 else
2561 {
2562 return Fail("%s: Unsupported number of operation inputs", __func__);
2563 }
2564
2565 desc.m_BiasEnabled = true;
2566 Optional<TensorInfo> biases(bias.GetInfo());
2567
2568 bool isSupported = false;
2569 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2570 IsTransposeConvolution2dSupported,
2571 data.m_Backends,
2572 isSupported,
2573 inputInfo,
2574 outputInfo,
2575 desc,
2576 weights.GetInfo(),
2577 biases);
2578 if (!isSupported)
2579 {
2580 return false;
2581 }
2582
2583 IConnectableLayer* startLayer =
2584 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
2585 if (!startLayer)
2586 {
2587 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2588 }
2589
2590 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2591 if (!endLayer)
2592 {
2593 return Fail("%s: ProcessActivation failed", __func__);
2594 }
2595
2596 input.Connect(startLayer->GetInputSlot(0));
2597
2598 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2599}
2600
2601} // armnn_driver namespace