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