blob: 61daeef5befdc893e29a8c38ac13903adf7f721f [file] [log] [blame]
Mike Kellyb5fdf382019-06-11 16:35:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "HalPolicy.hpp"
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01007#include "Utils.hpp"
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01008
Teresa Charlin8f6429d2019-10-01 13:10:15 +01009#include <armnn/TypesUtils.hpp>
10
Matteo Martincigh00d6ed12019-11-28 17:13:24 +000011#include <armnnUtils/DataLayoutIndexed.hpp>
12#include <armnnUtils/TensorUtils.hpp>
13
14#include <Half.hpp>
15
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010016#include <cmath>
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +000017#include <string>
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010018
Mike Kellyb5fdf382019-06-11 16:35:25 +010019namespace armnn_driver
20{
21namespace hal_1_2
22{
23
Teresa Charlin8f6429d2019-10-01 13:10:15 +010024using namespace armnn;
25
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +000026namespace
27{
28
29bool IsQSymmDequantizeForWeights(const Operation& operation, const Model& model)
30{
31 const Operand* operand = GetInputOperand<hal_1_2::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<hal_1_2::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 < model.operations.size(); ++operationIdx)
52 {
53 const auto& operationIt = model.operations[operationIdx];
54 switch (operationIt.type)
55 {
56 case HalPolicy::OperationType::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 HalPolicy::OperationType::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
81} // anonymous namespace
82
Mike Kellyb5fdf382019-06-11 16:35:25 +010083bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
84{
Mike Kellyb5fdf382019-06-11 16:35:25 +010085 switch (operation.type)
86 {
Kevin May407718f2019-09-09 14:46:41 +010087 case V1_2::OperationType::ABS:
josh minor00a963b2020-01-08 11:55:35 -060088 return ConvertElementwiseUnary(operation, model, data, UnaryOperation::Abs);
Mike Kelly46272802019-08-14 17:00:48 +010089 case V1_2::OperationType::ADD:
90 return ConvertAdd(operation, model, data);
Francis Murtagh19fa0cc2019-11-19 12:06:47 +000091 case V1_2::OperationType::ARGMAX:
92 return ConvertArgMinMax(operation, model, data, ArgMinMaxFunction::Max);
93 case V1_2::OperationType::ARGMIN:
94 return ConvertArgMinMax(operation, model, data, ArgMinMaxFunction::Min);
Sadik Armagan15d63e22019-07-26 16:59:35 +010095 case V1_2::OperationType::AVERAGE_POOL_2D:
96 return ConvertAveragePool2d(operation, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +010097 case V1_2::OperationType::BATCH_TO_SPACE_ND:
98 return ConvertBatchToSpaceNd(operation, model, data);
Mike Kellyb8805202019-07-31 17:25:43 +010099 case V1_2::OperationType::CONCATENATION:
100 return ConvertConcatenation(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100101 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100102 return ConvertConv2d(operation, model, data);
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100103 case V1_2::OperationType::DEPTH_TO_SPACE:
104 return ConvertDepthToSpace(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100105 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100106 return ConvertDepthwiseConv2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100107 case V1_2::OperationType::DEQUANTIZE:
108 return ConvertDequantize(operation, model, data);
109 case V1_2::OperationType::DIV:
110 return ConvertDiv(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000111 case V1_2::OperationType::EQUAL:
112 return ConvertComparison(operation, model, data, ComparisonOperation::Equal);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100113 case V1_2::OperationType::EXPAND_DIMS:
114 return ConvertExpandDims(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100115 case V1_2::OperationType::FLOOR:
116 return ConvertFloor(operation, model, data);
117 case V1_2::OperationType::FULLY_CONNECTED:
118 return ConvertFullyConnected(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000119 case V1_2::OperationType::GREATER:
120 return ConvertComparison(operation, model, data, ComparisonOperation::Greater);
121 case V1_2::OperationType::GREATER_EQUAL:
122 return ConvertComparison(operation, model, data, ComparisonOperation::GreaterOrEqual);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100123 case V1_2::OperationType::GROUPED_CONV_2D:
124 return ConvertGroupedConv2d(operation, model, data);
Aron Virginas-Tara2a73802019-10-09 15:30:40 +0100125 case V1_2::OperationType::INSTANCE_NORMALIZATION:
126 return ConvertInstanceNormalization(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100127 case V1_2::OperationType::L2_NORMALIZATION:
128 return ConvertL2Normalization(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100129 case V1_2::OperationType::L2_POOL_2D:
130 return ConvertL2Pool2d(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000131 case V1_2::OperationType::LESS:
132 return ConvertComparison(operation, model, data, ComparisonOperation::Less);
133 case V1_2::OperationType::LESS_EQUAL:
134 return ConvertComparison(operation, model, data, ComparisonOperation::LessOrEqual);
Mike Kelly46272802019-08-14 17:00:48 +0100135 case V1_2::OperationType::LOCAL_RESPONSE_NORMALIZATION:
136 return ConvertLocalResponseNormalization(operation, model, data);
137 case V1_2::OperationType::LOGISTIC:
138 return ConvertLogistic(operation, model, data);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +0100139 case V1_2::OperationType::LOG_SOFTMAX:
140 return ConvertLogSoftmax(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100141 case V1_2::OperationType::LSTM:
142 return ConvertLstm(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100143 case V1_2::OperationType::MAX_POOL_2D:
144 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100145 case V1_2::OperationType::MAXIMUM:
146 return ConvertMaximum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100147 case V1_2::OperationType::MEAN:
148 return ConvertMean(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100149 case V1_2::OperationType::MINIMUM:
150 return ConvertMinimum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100151 case V1_2::OperationType::MUL:
152 return ConvertMul(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000153 case V1_2::OperationType::NOT_EQUAL:
154 return ConvertComparison(operation, model, data, ComparisonOperation::NotEqual);
Mike Kelly3c673942019-07-25 09:26:06 +0100155 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100156 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100157 case V1_2::OperationType::PAD_V2:
158 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100159 case V1_2::OperationType::PRELU:
160 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +0100161 case V1_2::OperationType::QUANTIZE:
162 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100163 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
164 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100165 case V1_2::OperationType::RELU:
166 return ConvertReLu(operation, model, data);
167 case V1_2::OperationType::RELU1:
168 return ConvertReLu1(operation, model, data);
169 case V1_2::OperationType::RELU6:
170 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100171 case V1_2::OperationType::RESHAPE:
172 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100173 case V1_2::OperationType::RESIZE_BILINEAR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100174 return ConvertResize(operation, model, data, ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100175 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100176 return ConvertResize(operation, model, data, ResizeMethod::NearestNeighbor);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +0100177 case V1_2::OperationType::RSQRT:
josh minor00a963b2020-01-08 11:55:35 -0600178 return ConvertElementwiseUnary(operation, model, data, UnaryOperation::Rsqrt);
Sadik Armagan701d9a02019-09-04 15:16:18 +0100179 case V1_2::OperationType::SQRT:
180 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100181 case V1_2::OperationType::SQUEEZE:
182 return ConvertSqueeze(operation, model, data);
183 case V1_2::OperationType::STRIDED_SLICE:
184 return ConvertStridedSlice(operation, model, data);
185 case V1_2::OperationType::TRANSPOSE:
186 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +0100187 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +0100188 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +0100189 case V1_2::OperationType::SOFTMAX:
190 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +0100191 case V1_2::OperationType::SPACE_TO_BATCH_ND :
192 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100193 case V1_2::OperationType::SPACE_TO_DEPTH:
194 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100195 case V1_2::OperationType::SUB:
196 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100197 case V1_2::OperationType::TANH:
198 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100199 default:
200 return Fail("%s: Operation type %s not supported in ArmnnDriver",
201 __func__, toString(operation.type).c_str());
202 }
203}
204
Mike Kelly46272802019-08-14 17:00:48 +0100205bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
206{
207 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
208 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
209}
210
Francis Murtagh19fa0cc2019-11-19 12:06:47 +0000211bool HalPolicy::ConvertArgMinMax(const V1_2::Operation& operation,
212 const V1_2::Model& model,
213 ConversionData& data,
214 armnn::ArgMinMaxFunction argMinMaxFunction)
215{
216 ALOGV("hal_1_2::HalPolicy::ConvertArgMinMax()");
217 return ::ConvertArgMinMax<hal_1_2::HalPolicy>(operation, model, data, argMinMaxFunction);
218}
219
Sadik Armagan15d63e22019-07-26 16:59:35 +0100220bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
221{
222 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100223 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Average, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100224}
225
Finn Williams23b87b32019-07-30 11:44:05 +0100226bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
227{
228 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
229 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
230}
231
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000232bool HalPolicy::ConvertComparison(const Operation& operation,
233 const Model& model,
234 ConversionData& data,
235 ComparisonOperation comparisonOperation)
236{
237 ALOGV("hal_1_2::HalPolicy::ConvertComparison()");
238 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
239
240 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
241 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
242
243 if (!(input0.IsValid() && input1.IsValid()))
244 {
245 return Fail("%s: Operation has invalid inputs", __func__);
246 }
247
248 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
249 if (!output)
250 {
251 return Fail("%s: Could not read output 0", __func__);
252 }
253
254 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
255 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
256 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
257
258 if (IsDynamicTensor(outputInfo))
259 {
260 return Fail("%s: Dynamic output tensors are not supported", __func__);
261 }
262
263 ComparisonDescriptor descriptor(comparisonOperation);
264
265 bool isSupported = false;
266 FORWARD_LAYER_SUPPORT_FUNC(__func__,
267 IsComparisonSupported,
268 data.m_Backends,
269 isSupported,
270 inputInfo0,
271 inputInfo1,
272 outputInfo,
273 descriptor);
274
275 if (!isSupported)
276 {
277 return false;
278 }
279
280 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
281 assert(layer != nullptr);
282
283 input0.Connect(layer->GetInputSlot(0));
284 input1.Connect(layer->GetInputSlot(1));
285
286 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
287}
288
Mike Kellyb8805202019-07-31 17:25:43 +0100289bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
290{
291 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
292 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
293}
294
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100295bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
296{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100297 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
298
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100299 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
300 if (!input.IsValid())
301 {
302 return Fail("%s: Operation has invalid inputs", __func__);
303 }
304
305 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
306 if (!output)
307 {
308 return Fail("%s: Could not read output 0", __func__);
309 }
310
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100311 const TensorInfo& inputInfo = input.GetTensorInfo();
312 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100313
314 if (IsDynamicTensor(outputInfo))
315 {
316 return Fail("%s: Dynamic output tensors are not supported", __func__);
317 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100318
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100319 Convolution2dDescriptor desc;
320 desc.m_DataLayout = DataLayout::NHWC;
Mike Kellye1d60bb2019-07-11 11:44:52 +0100321
322 // Determine whether padding is implicit or explicit
323 bool implicitPadding = operation.inputs.size() == 7 ||
324 (operation.inputs.size() >= 8 &&
325 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
326
327 if (implicitPadding)
328 {
329 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
330 }
331 else if (operation.inputs.size() >= 10)
332 {
333 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
334 }
335
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100336 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
Mike Kellye1d60bb2019-07-11 11:44:52 +0100337
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100338 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100339 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
340 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
341 // the DataLayout is NCHW
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100342 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
Mike Kellye1d60bb2019-07-11 11:44:52 +0100343 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
344 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100345 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100346 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100347
348 if (!weightsPin.IsValid())
349 {
350 return Fail("%s: Operation has invalid weights", __func__);
351 }
352
353 if (!biasPin.IsValid())
354 {
355 return Fail("%s: Operation has invalid biases", __func__);
356 }
357
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100358 ConstTensor weights = weightsPin.GetConstTensor();
359 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100360 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
361
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100362 ActivationFn activation;
363
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100364 if (implicitPadding)
365 {
366 android::nn::PaddingScheme paddingScheme;
367 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
368 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
369 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
370 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
371 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
372 {
373 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
374 }
375
Mike Kellye1d60bb2019-07-11 11:44:52 +0100376 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
377 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
378 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
379 const uint32_t kernelX = weights.GetShape()[widthIndex];
380 const uint32_t kernelY = weights.GetShape()[heightIndex];
381 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
382 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100383
Mike Kelly86b36d42019-07-12 16:39:33 +0100384 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
385 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100386
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100387 }
388 else if (operation.inputs.size() >= 10)
389 {
390 // explicit padding
391 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
392 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
393 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
394 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
395 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
396 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
397 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
398 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
399 {
400 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
401 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100402 }
403 else
404 {
405 return Fail("%s: Unsupported number of operation inputs", __func__);
406 }
407
408 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100409 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100410
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100411 bool isSupported = false;
412 FORWARD_LAYER_SUPPORT_FUNC(__func__,
413 IsConvolution2dSupported,
414 data.m_Backends,
415 isSupported,
416 inputInfo,
417 outputInfo,
418 desc,
419 weights.GetInfo(),
420 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100421
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100422 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100423 {
424 return false;
425 }
426
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100427 IConnectableLayer* startLayer =
428 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100429
430 if (!startLayer)
431 {
432 return Fail("%s: AddConvolution2dLayer failed", __func__);
433 }
434
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100435 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100436
437 if (!endLayer)
438 {
439 return Fail("%s: ProcessActivation failed", __func__);
440 }
441
442 input.Connect(startLayer->GetInputSlot(0));
443
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100444 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100445}
446
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100447bool HalPolicy::ConvertDepthToSpace(const Operation& operation, const Model& model, ConversionData& data)
448{
449 ALOGV("hal_1_2::HalPolicy::ConvertDepthToSpace()");
450 return ::ConvertDepthToSpace<hal_1_2::HalPolicy>(operation, model, data);
451}
452
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100453bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
454{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100455 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
456
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100457 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
458
459 if (!input.IsValid())
460 {
461 return Fail("%s: Operation has invalid inputs", __func__);
462 }
463
464 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
465
466 if (!output)
467 {
468 return Fail("%s: Could not read output 0", __func__);
469 }
470
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100471 const TensorInfo& inputInfo = input.GetTensorInfo();
472 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100473
474 if (IsDynamicTensor(outputInfo))
475 {
476 return Fail("%s: Dynamic output tensors are not supported", __func__);
477 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100478
479 // ArmNN does not currently support non-fixed weights or bias
480 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
481 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
482
483 if (weightsOperand == nullptr)
484 {
485 return Fail("%s: Operand is invalid", __func__);
486 }
Teresa Charlin3b959602019-10-31 17:05:47 +0000487 if ( weightsOperand->dimensions[0] != 1)
488 {
489 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
490 __func__, weightsOperand->dimensions[0] );
491 }
492
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100493 DepthwiseConvolution2dDescriptor desc;
494 desc.m_DataLayout = DataLayout::NHWC;
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100495
496 // Determine whether padding is implicit or explicit
497 bool implicitPadding = operation.inputs.size() == 8 ||
498 (operation.inputs.size() >= 9 &&
499 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
500
501 // Look ahead to find the optional DataLayout, if present
502 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
503 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
504
505 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
506 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
507 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
508 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
509
510 // Reinterpret weight data as [ H, W, I, M ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100511 TensorShape weightsShape({ weightsOperand->dimensions[1],
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100512 weightsOperand->dimensions[2],
513 inputInfo.GetShape()[channelsIndex],
514 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
515
516 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100517 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100518
519 const ConstTensorPin weightsPin =
520 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
521 1,
522 model,
523 data,
524 HWIMToMIHW,
525 &weightsShape);
526
527 // Bias is a 1D tensor
528 const ConstTensorPin biasPin =
529 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
530
531 if (!weightsPin.IsValid())
532 {
533 return Fail("%s: Operation has invalid weights", __func__);
534 }
535
536 if (!biasPin.IsValid())
537 {
538 return Fail("%s: Operation has invalid biases", __func__);
539 }
540
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100541 ConstTensor weights = weightsPin.GetConstTensor();
542 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100543 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
544
545 ActivationFn activation;
546
547 if (implicitPadding)
548 {
549 android::nn::PaddingScheme paddingScheme;
550 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
551 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
552 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
553 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
554 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
555 {
556 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
557 }
558
559 const uint32_t kernelX = weights.GetShape()[3];
560 const uint32_t kernelY = weights.GetShape()[2];
561 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
562 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
563
Mike Kelly86b36d42019-07-12 16:39:33 +0100564 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
565 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100566 }
567 else if (operation.inputs.size() >= 11)
568 {
569 // explicit padding
570 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
571 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
572 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
573 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
574 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
575 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
576 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
577 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
578 {
579 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
580 }
581 }
582 else
583 {
584 return Fail("%s: Unsupported number of operation inputs", __func__);
585 }
586
587 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100588 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100589
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100590 bool isSupported = false;
591 FORWARD_LAYER_SUPPORT_FUNC(__func__,
592 IsDepthwiseConvolutionSupported,
593 data.m_Backends,
594 isSupported,
595 inputInfo,
596 outputInfo,
597 desc,
598 weights.GetInfo(),
599 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100600
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100601 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100602 {
603 return false;
604 }
605
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100606 IConnectableLayer* startLayer =
607 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100608
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100609 if (!startLayer)
610 {
611 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
612 }
613
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100614 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100615 if (!endLayer)
616 {
617 return Fail("%s: ProcessActivation failed", __func__);
618 }
619
620 input.Connect(startLayer->GetInputSlot(0));
621
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100622 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100623}
624
Mike Kelly46272802019-08-14 17:00:48 +0100625bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
626{
627 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000628
629 if (IsQSymmDequantizeForWeights(operation, model))
630 {
631 // NOTE: QSymm8 weights are dequantized internally by the driver,
632 // therefore this type of Dequantize is implicitly supported
633 return true;
634 }
635
Mike Kelly46272802019-08-14 17:00:48 +0100636 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
637}
638
639bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
640{
641 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
642 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
643}
644
josh minor00a963b2020-01-08 11:55:35 -0600645bool HalPolicy::ConvertElementwiseUnary(const Operation& operation,
646 const Model& model,
647 ConversionData& data,
648 UnaryOperation unaryOperation)
649{
650 ALOGV("hal_1_2::HalPolicy::ConvertElementwiseUnary()");
651 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
652
653 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
654
655 if (!input.IsValid())
656 {
657 return Fail("%s: Operation has invalid input", __func__);
658 }
659
660 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
661 if (!output)
662 {
663 return Fail("%s: Could not read output 0", __func__);
664 }
665
666 const TensorInfo& inputInfo = input.GetTensorInfo();
667 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
668
669 if (IsDynamicTensor(outputInfo))
670 {
671 return Fail("%s: Dynamic output tensors are not supported", __func__);
672 }
673
674 ElementwiseUnaryDescriptor descriptor(unaryOperation);
675
676 bool isSupported = false;
677 FORWARD_LAYER_SUPPORT_FUNC(__func__,
678 IsElementwiseUnarySupported,
679 data.m_Backends,
680 isSupported,
681 inputInfo,
682 outputInfo,
683 descriptor);
684
685 if (!isSupported)
686 {
687 return false;
688 }
689
690 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
691 assert(layer != nullptr);
692
693 input.Connect(layer->GetInputSlot(0));
694
695 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
696}
697
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100698bool HalPolicy::ConvertExpandDims(const Operation& operation, const Model& model, ConversionData& data)
699{
700 ALOGV("hal_1_2::HalPolicy::ConvertExpandDims()");
701
702 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
703
704 if (!input.IsValid())
705 {
706 return Fail("%s: Operation has invalid input", __func__);
707 }
708
709 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
710 if (!output)
711 {
712 return Fail("%s: Operation has invalid output", __func__);
713 }
714
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100715 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100716 if (IsDynamicTensor(outputInfo))
717 {
718 return Fail("%s: Dynamic output tensors are not supported", __func__);
719 }
720
721 int32_t axis;
722 if (!GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, axis, model, data))
723 {
724 return Fail("%s: failed to get axis input value", __func__);
725 }
726
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100727 TensorShape targetShape;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100728
729 try
730 {
731 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
732 }
733 catch (const std::exception &e)
734 {
735 return Fail("%s: %s", __func__, e.what());
736 }
737
738 if (targetShape != outputInfo.GetShape())
739 {
740 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
741 }
742
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100743 ReshapeDescriptor reshapeDescriptor;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100744 reshapeDescriptor.m_TargetShape = targetShape;
745
746 bool isSupported = false;
747 FORWARD_LAYER_SUPPORT_FUNC(__func__,
748 IsReshapeSupported,
749 data.m_Backends,
750 isSupported,
751 input.GetTensorInfo(),
752 reshapeDescriptor);
753
754 if (!isSupported)
755 {
756 return false;
757 }
758
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100759 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100760 assert(layer != nullptr);
761 input.Connect(layer->GetInputSlot(0));
762
763 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
764}
765
Mike Kelly46272802019-08-14 17:00:48 +0100766bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
767{
768 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
769 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
770}
771
772bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
773{
774 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
775 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
776}
777
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100778bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
779{
780 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
781
782 //
783 // Parse data
784 //
785 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
786 if (!input.IsValid())
787 {
788 return Fail("%s: Operation has invalid inputs", __func__);
789 }
790 const TensorInfo& inputInfo = input.GetTensorInfo();
791
792 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
793 if (!output)
794 {
795 return Fail("%s: Could not read output 0", __func__);
796 }
797 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
798 if (IsDynamicTensor(outputInfo))
799 {
800 return Fail("%s: Dynamic output tensors are not supported", __func__);
801 }
802
803 // Look ahead to determine data layout
804 DataLayout dataLayout = DataLayout::NHWC;
805 if (operation.inputs.size() == 12)
806 {
807 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
808 }
809 else
810 {
811 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
812 }
813
814 // NOTE:
815 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
816 // but Arm NN expects the filter's height and width indices to match the input's height and
817 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
818 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
819 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
820 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
821 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
822 const ConstTensorPin biasesPin =
823 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
824 if (!weightsPin.IsValid() || !biasesPin.IsValid())
825 {
826 return Fail("%s: Operation has invalid inputs", __func__);
827 }
828
829 ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000830 ConstTensor biases = biasesPin.GetConstTensor();
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100831 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
832
833 const TensorShape& inputShape = inputInfo.GetShape();
834 const TensorShape& outputShape = outputInfo.GetShape();
835 const TensorShape& weightsShape = weights.GetShape();
836 const TensorShape& biasesShape = biases.GetShape();
837
838 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
839 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
840 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
841 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
842
843 Convolution2dDescriptor desc;
844 desc.m_DataLayout = dataLayout;
845 desc.m_BiasEnabled = true;
846
847 int numGroups;
848 ActivationFn activation;
849
850 if (operation.inputs.size() == 12)
851 {
852 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
853 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
854 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
855 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
856 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
857 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
858 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
859 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
860 {
861 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
862 }
863
864 }
865 else if (operation.inputs.size() == 9)
866 {
867 android::nn::PaddingScheme paddingScheme;
868 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
869 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
870 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
871 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
872 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
873 {
874 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
875 }
876
877 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
878 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
879
880 const uint32_t kernelX = weightsShape[widthIndex];
881 const uint32_t kernelY = weightsShape[heightIndex];
882
883 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
884 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
885 }
886 else
887 {
888 return Fail("%s: Unsupported number of operation inputs", __func__);
889 }
890
891 const unsigned int outputChannels = outputShape[channelsIndex];
892
893 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
894 const unsigned int channelMultiplier = outputChannels / numGroups;
895
896 //
897 // Validate all relevant inputs
898 //
899 if (numGroups <= 0)
900 {
901 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
902 }
903
904 if (outputChannels % numGroups != 0u)
905 {
906 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
907 }
908
909 //
910 // Set up Splitter layer
911 //
912 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
913 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
914
915 TensorInfo splitterOutputInfo(4,
916 splitterDimSizes,
917 inputInfo.GetDataType(),
918 inputInfo.GetQuantizationScale(),
919 inputInfo.GetQuantizationOffset());
920
921 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
922
923 ViewsDescriptor splitterDesc(numGroups);
924 for (unsigned int group = 0u; group < numGroups; ++group)
925 {
926 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
927 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
928 {
929 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
930 }
931 }
932
933 bool isSupported = false;
934 FORWARD_LAYER_SUPPORT_FUNC(__func__,
935 IsSplitterSupported,
936 data.m_Backends,
937 isSupported,
938 inputInfo,
939 splitterOutputInfos,
940 splitterDesc);
941 if (!isSupported)
942 {
943 return false;
944 }
945
946 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
947 if (!splitterLayer)
948 {
949 return Fail("%s: Failed to add SplitterLayer", __func__);
950 }
951
952 input.Connect(splitterLayer->GetInputSlot(0));
953 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
954 {
955 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
956 }
957
958 //
959 // Set up Convolution2d layers for each group
960 //
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000961
962 // Set up group tensor shapes
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100963 TensorShape groupInputShape(inputShape);
964 groupInputShape[channelsIndex] = channelsPerGroup;
965
966 TensorShape groupOutputShape(outputShape);
967 groupOutputShape[channelsIndex] = 1;
968
969 TensorShape groupWeightsShape(weightsShape);
970 groupWeightsShape[0] /= channelMultiplier * numGroups;
971
972 TensorShape groupBiasesShape({ 1 });
973
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000974 // Set up group tensor infos
975 TensorInfo groupInputInfo(inputInfo);
976 groupInputInfo.SetShape(groupInputShape);
977
978 const TensorInfo& weightsInfo = weights.GetInfo();
979 TensorInfo groupWeightsInfo(weightsInfo);
980 groupWeightsInfo.SetShape(groupWeightsShape);
981
982 const TensorInfo& biasesInfo = biases.GetInfo();
983 TensorInfo groupBiasesInfo(biasesInfo);
984 groupBiasesInfo.SetShape(groupBiasesShape);
985
986 TensorInfo groupOutputInfo(outputInfo);
987 groupOutputInfo.SetShape(groupOutputShape);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100988
989 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
990 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
991
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000992 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100993 for (unsigned int group = 0u; group < numGroups; ++group)
994 {
995 for (unsigned int m = 0u; m < channelMultiplier; ++m)
996 {
997 auto index = group * channelMultiplier + m;
998
999 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1000 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1001
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +00001002 if (weightsInfo.HasPerAxisQuantization())
1003 {
1004 // Extract per-axis quantization scales for group weights
1005 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1006 groupWeightsInfo.SetQuantizationScales(
1007 std::vector<float>(weightsQuantScales.begin() + index,
1008 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1009
1010 // Extract per-axis quantization scales for group biases
1011 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1012 groupBiasesInfo.SetQuantizationScales(
1013 std::vector<float>(biasesQuantScales.begin() + index,
1014 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1015 }
1016
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001017 // Extract weights and biases data for current group convolution
1018 ConstTensor groupWeights(groupWeightsInfo,
1019 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1020 weightsDataOffset));
1021 ConstTensor groupBiases(groupBiasesInfo,
1022 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1023 biasesDataOffset));
1024
1025 isSupported = false;
1026 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1027 IsConvolution2dSupported,
1028 data.m_Backends,
1029 isSupported,
1030 groupInputInfo,
1031 groupOutputInfo,
1032 desc,
1033 groupWeightsInfo,
1034 Optional<TensorInfo>(groupBiasesInfo));
1035 if (!isSupported)
1036 {
1037 return false;
1038 }
1039
1040 IConnectableLayer *convLayer =
1041 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1042 if (!convLayer)
1043 {
1044 return Fail("%s: AddConvolution2dLayer failed", __func__);
1045 }
1046
1047 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1048 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1049
1050 convLayers[index] = convLayer;
1051 }
1052 }
1053
1054 //
1055 // Set up Concat layer
1056 //
1057 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
1058 for (unsigned int group = 0u; group < numGroups; ++group)
1059 {
1060 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1061 {
1062 auto index = group * channelMultiplier + m;
1063 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1064 concatDescriptor.SetConcatAxis(channelsIndex);
1065 }
1066 }
1067
1068 isSupported = false;
1069 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1070 IsConcatSupported,
1071 data.m_Backends,
1072 isSupported,
1073 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1074 outputInfo,
1075 concatDescriptor);
1076 if (!isSupported)
1077 {
1078 return false;
1079 }
1080
1081 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1082 if (!concatLayer)
1083 {
1084 return Fail("%s: AddConcatLayer failed", __func__);
1085 }
1086
1087 for (unsigned int group = 0u; group < numGroups; ++group)
1088 {
1089 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1090 {
1091 auto index = group * channelMultiplier + m;
1092 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1093 }
1094 }
1095 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1096
1097 //
1098 // Set up Activation layer (if it is set)
1099 //
1100 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
1101 if (!endLayer)
1102 {
1103 return Fail("%s: ProcessActivation failed", __func__);
1104 }
1105
1106 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1107}
1108
Aron Virginas-Tara2a73802019-10-09 15:30:40 +01001109bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
1110{
1111 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
1112
1113 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1114 if (!input.IsValid())
1115 {
1116 return Fail("%s: Operation has an invalid input 0", __func__);
1117 }
1118
1119 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1120 if (!output)
1121 {
1122 return Fail("%s: Operation has an invalid output", __func__);
1123 }
1124
1125 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1126 if (IsDynamicTensor(outputInfo))
1127 {
1128 return Fail("%s: Dynamic output tensors are not supported", __func__);
1129 }
1130
1131 // Determine data type of input tensor
1132 OperandType inputType;
1133 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1134 {
1135 return Fail("%s: Operation has invalid inputs", __func__);
1136 }
1137
1138 InstanceNormalizationDescriptor desc;
1139
1140 // Read gamma, beta & epsilon
1141 if (inputType == OperandType::TENSOR_FLOAT16)
1142 {
1143 Half fp16Gamma;
1144 Half fp16Beta;
1145 Half fp16Epsilon;
1146
1147 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1148 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1149 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1150 {
1151 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1152 }
1153
1154 desc.m_Gamma = static_cast<float>(fp16Gamma);
1155 desc.m_Beta = static_cast<float>(fp16Beta);
1156 desc.m_Eps = static_cast<float>(fp16Epsilon);
1157 }
1158 else if (inputType == OperandType::TENSOR_FLOAT32)
1159 {
1160 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1161 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1162 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1163 {
1164 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1165 }
1166 }
1167 else
1168 {
1169 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1170 }
1171
1172 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1173
1174 bool isSupported = false;
1175 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1176 IsInstanceNormalizationSupported,
1177 data.m_Backends,
1178 isSupported,
1179 input.GetTensorInfo(),
1180 outputInfo,
1181 desc);
1182 if (!isSupported)
1183 {
1184 return false;
1185 }
1186
1187 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1188 input.Connect(layer->GetInputSlot(0));
1189
1190 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1191}
1192
Mike Kelly46272802019-08-14 17:00:48 +01001193bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1194{
1195 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1196 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1197}
1198
Sadik Armagan15d63e22019-07-26 16:59:35 +01001199bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1200{
1201 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001202 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001203}
1204
Mike Kelly46272802019-08-14 17:00:48 +01001205bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1206 const Model& model,
1207 ConversionData& data)
1208{
1209 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1210 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1211}
1212
1213bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1214{
1215 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1216 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1217}
1218
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001219bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1220{
1221 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1222
1223 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1224 if (!input.IsValid())
1225 {
1226 return Fail("%s: Failed to read input 0", __func__);
1227 }
1228
1229 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1230 if (!output)
1231 {
1232 return Fail("%s: Failed to read output", __func__);
1233 }
1234
1235 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1236 if (IsDynamicTensor(outputInfo))
1237 {
1238 return Fail("%s: Dynamic output tensors are not supported", __func__);
1239 }
1240
1241 // Determine data type of input tensor
1242 OperandType inputType;
1243 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1244 {
1245 return Fail("%s: Operation has invalid inputs", __func__);
1246 }
1247
1248 LogSoftmaxDescriptor descriptor;
1249
1250 // Read beta
1251 if (inputType == OperandType::TENSOR_FLOAT16)
1252 {
1253 Half fp16Beta;
1254 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1255 {
1256 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1257 }
1258
1259 descriptor.m_Beta = static_cast<float>(fp16Beta);
1260 }
1261 else if (inputType == OperandType::TENSOR_FLOAT32)
1262 {
1263 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1264 {
1265 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1266 }
1267 }
1268 else
1269 {
1270 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1271 }
1272
1273 // Read axis
1274 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1275 {
1276 return Fail("%s: Failed to read input 2", __func__);
1277 }
1278
1279 bool isSupported = false;
1280 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1281 IsLogSoftmaxSupported,
1282 data.m_Backends,
1283 isSupported,
1284 input.GetTensorInfo(),
1285 outputInfo,
1286 descriptor);
1287 if (!isSupported)
1288 {
1289 return false;
1290 }
1291
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001292 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001293 if (!layer)
1294 {
1295 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1296 }
1297
1298 input.Connect(layer->GetInputSlot(0));
1299
1300 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1301}
1302
Sadik Armagan15d63e22019-07-26 16:59:35 +01001303bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1304{
1305 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001306 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001307}
1308
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001309bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1310{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001311 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1312
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001313 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1314 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1315
1316 if (!input0.IsValid() || !input1.IsValid())
1317 {
1318 return Fail("%s: Operation has invalid inputs", __func__);
1319 }
1320
1321 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1322 if (!outputOperand)
1323 {
1324 return Fail("%s: Could not read output", __func__);
1325 }
1326
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001327 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001328 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001329 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001330 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001331 }
1332
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001333 bool isSupported = false;
1334 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1335 IsMaximumSupported,
1336 data.m_Backends,
1337 isSupported,
1338 input0.GetTensorInfo(),
1339 input1.GetTensorInfo(),
1340 outInfo);
1341
1342 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001343 {
1344 return false;
1345 }
1346
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001347 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001348 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001349 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1350 if (!isReshapeSupported)
1351 {
1352 return false;
1353 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001354
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001355 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001356}
1357
Mike Kelly46272802019-08-14 17:00:48 +01001358bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1359{
1360 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1361 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1362}
1363
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001364bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1365{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001366 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1367
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001368 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1369 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1370
1371 if (!input0.IsValid() || !input1.IsValid())
1372 {
1373 return Fail("%s: Operation has invalid inputs", __func__);
1374 }
1375
1376 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1377 if (!output)
1378 {
1379 return Fail("%s: Could not read output 0", __func__);
1380 }
1381
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001382 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001383 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001384 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001385 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001386 }
1387
1388 bool isSupported = false;
1389 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1390 IsMinimumSupported,
1391 data.m_Backends,
1392 isSupported,
1393 input0.GetTensorInfo(),
1394 input1.GetTensorInfo(),
1395 outputInfo);
1396
1397 if (!isSupported)
1398 {
1399 return false;
1400 }
1401
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001402 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001403 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001404 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1405 if (!isReshapeSupported)
1406 {
1407 return false;
1408 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001409
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001410 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001411}
1412
Mike Kelly46272802019-08-14 17:00:48 +01001413bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1414{
1415 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1416 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1417}
1418
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001419bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1420{
1421 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1422 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1423}
1424
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001425bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1426{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001427 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1428
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001429 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1430 if (!input.IsValid())
1431 {
1432 return Fail("%s: Could not read input 0", __func__);
1433 }
1434
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001435 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1436 if (!output)
1437 {
1438 return Fail("%s: Could not read output", __func__);
1439 }
1440
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001441 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001442 unsigned int rank = inputInfo.GetNumDimensions();
1443
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001444 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001445 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1446 {
1447 return Fail("%s: Could not convert paddings", __func__);
1448 }
1449
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001450 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001451 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001452 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001453 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001454 }
1455
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001456 // Determine type of padding value
1457 OperandType operandType0;
1458 OperandType operandType2;
1459
1460 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1461 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1462 {
1463 return Fail("%s: Operation has invalid inputs", __func__);
1464 }
1465
1466 // Read value to use for padding
1467 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1468 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001469 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001470 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1471 {
1472 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1473 }
1474
1475 descriptor.m_PadValue = f16PadValue;
1476 }
1477 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1478 {
1479 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1480 {
1481 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1482 }
1483 }
1484 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1485 {
Mike Kelly3c673942019-07-25 09:26:06 +01001486 int32_t intPadValue = 0;
1487 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001488 {
1489 return Fail("%s: Could not read input 2 (INT32)", __func__);
1490 }
Mike Kelly3c673942019-07-25 09:26:06 +01001491 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001492 }
1493 else
1494 {
1495 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1496 }
1497
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001498 bool isSupported = false;
1499 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1500 IsPadSupported,
1501 data.m_Backends,
1502 isSupported,
1503 inputInfo,
1504 outputInfo,
1505 descriptor);
1506 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001507 {
1508 return false;
1509 }
1510
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001511 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001512 assert(layer != nullptr);
1513 input.Connect(layer->GetInputSlot(0));
1514 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1515
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001516 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001517}
1518
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001519bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1520{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001521 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1522
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001523 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1524 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1525
1526 if (!input.IsValid() || !alpha.IsValid())
1527 {
1528 return Fail("%s: Operation has invalid inputs", __func__);
1529 }
1530
1531 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1532
1533 if (!output)
1534 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001535 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001536 }
1537
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001538 const TensorInfo& inputInfo = input.GetTensorInfo();
1539 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1540 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001541
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001542 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001543 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001544 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001545 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001546
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001547 bool isSupported = false;
1548 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1549 IsPreluSupported,
1550 data.m_Backends,
1551 isSupported,
1552 inputInfo,
1553 alphaInfo,
1554 outputInfo);
1555 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001556 {
1557 return false;
1558 }
1559
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001560 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001561
1562 if (!layer)
1563 {
1564 return Fail("%s: AddPreluLayer failed", __func__);
1565 }
1566
Sadik Armagan64b19b52019-08-19 09:49:58 +01001567 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1568 if (!isReshapeSupported)
1569 {
1570 return false;
1571 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001572
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001573 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001574}
1575
Sadik Armagan5a476a82019-07-30 09:43:18 +01001576bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1577{
1578 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1579
1580 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1581 if (!input.IsValid())
1582 {
1583 return Fail("%s: Operation has invalid input", __func__);
1584 }
1585
1586 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1587 if (!outputOperand)
1588 {
1589 return Fail("%s: Operation has invalid outputs", __func__);
1590 }
1591
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001592 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001593 if (IsDynamicTensor(outputInfo))
1594 {
1595 return Fail("%s: Dynamic output tensors are not supported", __func__);
1596 }
1597
1598 bool isSupported = false;
1599 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1600 IsQuantizeSupported,
1601 data.m_Backends,
1602 isSupported,
1603 input.GetTensorInfo(),
1604 outputInfo);
1605 if (!isSupported)
1606 {
1607 return false;
1608 }
1609
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001610 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001611 assert(layer != nullptr);
1612 input.Connect(layer->GetInputSlot(0));
1613
1614 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1615}
1616
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001617bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1618{
1619 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1620
1621 //Inputs:
1622 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1623 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1624 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1625 if (!input.IsValid())
1626 {
1627 return Fail("%s: Could not read input 0: input", __func__);
1628 }
1629
1630 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1631 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1632 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1633 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1634 if (!previousCellStateIn.IsValid())
1635 {
1636 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1637 }
1638
1639 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1640 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1641 // is quantized with a fixed quantization range of -1, 127/128.
1642 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1643 if (!previousOutputIn.IsValid())
1644 {
1645 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1646 }
1647
1648 // Get the input tensors:
1649 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1650 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1651 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1652 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001653 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001654
1655 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1656 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1657 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1658 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001659 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001660
1661 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1662 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1663 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1664 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001665 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001666
1667 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1668 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1669 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1670 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001671 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001672
1673 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1674 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1675 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1676 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001677 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001678
1679 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1680 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1681 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1682 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001683 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001684
1685 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1686 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1687 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1688 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001689 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001690
1691 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1692 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1693 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1694 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001695 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001696
1697 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1698 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1699 // of input and weights scales and zeroPoint equal to 0.
1700 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001701 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001702
1703 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1704 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1705 // of input and weights scales and zeroPoint equal to 0.
1706 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001707 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001708
1709 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1710 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1711 // and weights scales and zeroPoint equal to 0.
1712 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001713 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001714
1715 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1716 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1717 // of input and weights scales and zeroPoint equal to 0.
1718 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001719 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001720
1721 if (!inputToInputWeightsPin.IsValid() ||
1722 !inputToForgetWeightsPin.IsValid() ||
1723 !inputToCellWeightsPin.IsValid() ||
1724 !inputToOutputWeightsPin.IsValid() ||
1725 !recurrentToInputWeightsPin.IsValid() ||
1726 !recurrentToForgetWeightsPin.IsValid() ||
1727 !recurrentToCellWeightsPin.IsValid() ||
1728 !recurrentToOutputWeightsPin.IsValid() ||
1729 !inputGateBiasPin.IsValid() ||
1730 !forgetGateBiasPin.IsValid() ||
1731 !cellBiasPin.IsValid() ||
1732 !outputGateBiasPin.IsValid())
1733 {
1734 return Fail("%s: Operation has invalid tensor inputs", __func__);
1735 }
1736
1737 // Outputs:
1738 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1739 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1740 // of -2^4, 2^4 * 32767/32768.
1741 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1742 if (!cellStateOut)
1743 {
1744 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1745 }
1746
1747 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1748 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1749 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1750 if (!output)
1751 {
1752 return Fail("%s: Could not read output 1: output", __func__);
1753 }
1754
1755 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001756 const TensorInfo& inputInfo = input.GetTensorInfo();
1757 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1758 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001759
1760 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001761 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1762 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001763
1764 // Dynamic tensors currently not supported
1765 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1766 {
1767 return Fail("%s: Dynamic output tensors are not supported", __func__);
1768 }
1769
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001770 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001771
1772 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1773 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1774 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1775 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1776 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1777 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1778 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1779 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1780 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1781 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1782 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1783 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1784
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001785 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001786 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1787 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1788 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1789 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1790 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1791 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1792 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1793 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1794 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1795 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1796 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1797 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1798
1799 bool isSupported = false;
1800 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1801 IsQuantizedLstmSupported,
1802 data.m_Backends,
1803 isSupported,
1804 inputInfo,
1805 previousCellStateInInfo,
1806 previousOutputInInfo,
1807 cellStateOutInfo,
1808 outputInfo,
1809 paramsInfo);
1810
1811 if (!isSupported)
1812 {
1813 return false;
1814 }
1815
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001816 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001817 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001818 previousCellStateIn.Connect(layer->GetInputSlot(1));
1819 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001820
1821 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1822 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1823}
1824
Sadik Armagan61113162019-07-25 09:09:40 +01001825bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1826{
1827 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1828 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1829}
1830
1831bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1832{
1833 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1834 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1835}
1836
1837bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1838{
1839 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1840 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1841}
1842
Mike Kelly46272802019-08-14 17:00:48 +01001843bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1844{
1845 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1846 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1847}
1848
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001849bool HalPolicy::ConvertResize(const Operation& operation,
1850 const Model& model,
1851 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001852 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001853{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001854 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001855 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001856
1857 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001858 if (!input.IsValid())
1859 {
1860 return Fail("%s: Could not read input 0", __func__);
1861 }
1862
1863 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1864 if (!output)
1865 {
1866 return Fail("%s: Could not read output 0", __func__);
1867 }
1868
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001869 const TensorInfo& inputInfo = input.GetTensorInfo();
1870 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001871
1872 if (IsDynamicTensor(outputInfo))
1873 {
1874 return Fail("%s: Dynamic output tensors are not supported", __func__);
1875 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001876
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001877 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001878 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001879 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1880
1881 OperandType operandType1;
1882 OperandType operandType2;
1883
1884 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1885 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1886 {
1887 return Fail("%s: Operation has invalid inputs", __func__);
1888 }
1889
1890 if (operandType1 != operandType2)
1891 {
1892 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1893 }
1894
1895 if (operandType1 == OperandType::INT32)
1896 {
1897 // Case 1: resizing by shape
1898 int32_t targetWidth = 0;
1899 int32_t targetHeight = 0;
1900
1901 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1902 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1903 {
1904 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1905 }
1906
1907 if (targetWidth < 0 || targetHeight < 0)
1908 {
1909 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1910 "Target width/height cannot be < 0", __func__);
1911 }
1912
1913 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001914 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001915 }
1916 else if (operandType1 == OperandType::FLOAT32)
1917 {
1918 // Case 2: resizing by scale
1919 float widthScale = 1.0f;
1920 float heightScale = 1.0f;
1921
1922 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1923 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1924 {
1925 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1926 }
1927
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001928 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001929 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1930
1931 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1932 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1933
1934 descriptor.m_TargetWidth = std::floor(width * widthScale);
1935 descriptor.m_TargetHeight = std::floor(height * heightScale);
1936 }
Keith Davisd410b602019-12-19 12:31:30 +00001937 else if (operandType1 == OperandType::FLOAT16)
1938 {
1939 Half widthScale;
1940 Half heightScale;
1941
1942 if (!GetInputScalar<HalPolicy>(operation, 1, HalPolicy::OperandType::FLOAT16, widthScale, model, data) ||
1943 !GetInputScalar<HalPolicy>(operation, 2, HalPolicy::OperandType::FLOAT16, heightScale, model, data))
1944 {
1945 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1946 }
1947
1948 const TensorShape& inputShape = inputInfo.GetShape();
1949 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1950
1951 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
1952 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
1953
1954 descriptor.m_TargetWidth = std::floor(width * widthScale);
1955 descriptor.m_TargetHeight = std::floor(height * heightScale);
1956 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001957 else
1958 {
Keith Davisd410b602019-12-19 12:31:30 +00001959 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001960 }
1961
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001962 bool isSupported = false;
1963 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1964 IsResizeSupported,
1965 data.m_Backends,
1966 isSupported,
1967 inputInfo,
1968 outputInfo,
1969 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001970
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001971 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001972 {
1973 return false;
1974 }
1975
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001976 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001977
1978 assert(layer != nullptr);
1979
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001980 input.Connect(layer->GetInputSlot(0));
1981
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001982 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001983}
1984
Finn Williamsd74c5052019-07-30 17:06:00 +01001985bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1986{
1987 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1988 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1989}
1990
Keith Davisa6bc52f2019-06-26 09:39:49 +01001991bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1992{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001993 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001994
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001995 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001996 if (!input.IsValid() )
1997 {
1998 return Fail("%s: Operation has invalid inputs", __func__);
1999 }
2000
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002001 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01002002 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01002003 if (rank != 4)
2004 {
2005 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2006 }
2007
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002008 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2009 if (!output)
2010 {
2011 return Fail("%s: Could not read output 0", __func__);
2012 }
2013
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002014 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002015 if (IsDynamicTensor(outputInfo))
2016 {
2017 return Fail("%s: Dynamic output tensors are not supported", __func__);
2018 }
2019
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002020 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01002021
2022 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
2023
2024 if (desc.m_BlockSize <= 1)
2025 {
2026 return Fail("%s: Block size must be at least 1 in all dimensions");
2027 }
2028
2029 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
2030
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002031 bool isSupported = false;
2032 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2033 IsSpaceToDepthSupported,
2034 data.m_Backends,
2035 isSupported,
2036 inputInfo,
2037 outputInfo,
2038 desc);
2039 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01002040 {
2041 return false;
2042 }
2043
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002044 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002045 assert(layer != nullptr);
2046 input.Connect(layer->GetInputSlot(0));
2047
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002048 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002049}
2050
Francis Murtagh074c25a2019-07-22 16:40:57 +01002051bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
2052{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002053 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
2054
Francis Murtagh074c25a2019-07-22 16:40:57 +01002055 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2056 if (!input.IsValid())
2057 {
2058 return Fail("%s: Operation has invalid inputs", __func__);
2059 }
2060
2061 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2062 if (!outputOperand)
2063 {
2064 return Fail("%s: Operation has no outputs", __func__);
2065 }
2066
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002067 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01002068 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01002069 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002070 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002071 }
2072
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002073 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01002074 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
2075 {
2076 return Fail("%s: Operation has invalid inputs", __func__);
2077 }
2078
2079 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
2080 2,
2081 HalPolicy::OperandType::INT32,
2082 desc.m_Axis,
2083 model,
2084 data))
2085 {
2086 return Fail("%s: Operation has invalid inputs", __func__);
2087 }
2088
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01002089 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2090 !(desc.m_Axis == 1 ||
2091 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2092 {
2093 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2094 }
2095
Francis Murtagh074c25a2019-07-22 16:40:57 +01002096 bool isSupported = false;
2097 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2098 IsSoftmaxSupported,
2099 data.m_Backends,
2100 isSupported,
2101 input.GetTensorInfo(),
2102 outputInfo,
2103 desc);
2104 if (!isSupported)
2105 {
2106 return false;
2107 }
2108
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002109 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002110 assert(layer != nullptr);
2111 input.Connect(layer->GetInputSlot(0));
2112
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002113 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002114}
2115
Mike Kelly0a879362019-07-29 16:56:31 +01002116bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2117{
2118 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2119 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2120}
2121
Sadik Armagan61113162019-07-25 09:09:40 +01002122bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2123{
2124 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2125 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2126}
2127
Pablo Tello972603f2019-11-28 15:21:41 +00002128template<typename HalPolicy,
2129 typename HalOperation = typename HalPolicy::Operation,
2130 typename HalModel = typename HalPolicy::Model>
2131bool SetupAndTrackLayerOutputSlotAndOverrideTensorInfo(const HalOperation& operation,
2132 uint32_t operationOutputIndex,
2133 armnn::IConnectableLayer& layer,
2134 uint32_t layerOutputIndex,
2135 const HalModel& model,
2136 ConversionData& data,
2137 const armnn::TensorInfo tensor_info)
2138{
2139 using HalOperand = typename HalPolicy::Operand;
2140
2141 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
2142 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
2143 {
2144 return false;
2145 }
2146
2147 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
2148
2149 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
2150 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
2151
2152 outputSlot.SetTensorInfo(tensor_info);
2153
2154 return true;
2155}
2156
2157
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002158bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2159{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002160 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2161
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002162 // Inputs:
2163 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2164 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2165 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2166 if (!input.IsValid())
2167 {
2168 return Fail("%s: Could not read input 0: input", __func__);
2169 }
2170 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2171 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2172 if (!outputStateIn.IsValid())
2173 {
2174 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2175 }
2176 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2177 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2178 if (!cellStateIn.IsValid())
2179 {
2180 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2181 }
2182
2183 // Get the mandatory input tensors:
2184 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2185 // [num_units, input_size].
2186 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002187 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002188 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2189 // [num_units, input_size].
2190 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002191 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002192 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2193 // [num_units, input_size].
2194 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002195 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002196 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2197 // [num_units, output_size].
2198 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002199 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002200 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2201 // [num_units, output_size].
2202 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002203 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002204 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2205 // [num_units, output_size].
2206 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002207 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002208 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2209 const ConstTensorPin forgetGateBiasPin =
2210 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2211 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2212 const ConstTensorPin cellBiasPin =
2213 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2214 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2215 const ConstTensorPin outputGateBiasPin =
2216 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2217
2218 if (!inputToForgetWeightsPin.IsValid() ||
2219 !inputToCellWeightsPin.IsValid() ||
2220 !inputToOutputWeightsPin.IsValid() ||
2221 !recurrentToForgetWeightsPin.IsValid() ||
2222 !recurrentToCellWeightsPin.IsValid() ||
2223 !recurrentToOutputWeightsPin.IsValid() ||
2224 !forgetGateBiasPin.IsValid() ||
2225 !cellBiasPin.IsValid() ||
2226 !outputGateBiasPin.IsValid())
2227 {
2228 return Fail("%s: Operation has invalid tensor inputs", __func__);
2229 }
2230
2231 // Get the optional input tensors:
2232 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2233 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2234 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002235 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002236 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2237 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2238 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2239 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002240 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002241 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2242 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002243 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002244 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2245 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002246 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002247 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2248 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002249 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002250 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2251 const ConstTensorPin inputGateBiasPin =
2252 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2253 12,
2254 model,
2255 data,
2256 g_DontPermute,
2257 nullptr,
2258 true);
2259
2260 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2261 // [output_size, num_units].
2262 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002263 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002264 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2265 const ConstTensorPin projectionBiasPin =
2266 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2267 17,
2268 model,
2269 data,
2270 g_DontPermute,
2271 nullptr,
2272 true);
2273
2274 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2275 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2276 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2277 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2278 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2279 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2280 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2281 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2282 {
2283 return Fail("%s: Operation has invalid tensor inputs", __func__);
2284 }
2285
2286 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2287 // 20: The activation function: A value indicating the activation function:
2288 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2289 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2290 // If set to 0.0 then clipping is disabled.
2291 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2292 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2293 ActivationFn activation;
2294 float cellClip;
2295 float projClip;
2296 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2297 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2298 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2299 {
2300 return Fail("%s: Operation has invalid scalar inputs", __func__);
2301 }
2302
2303 // Get the normalization tensors
2304 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2305 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002306 const ConstTensorPin inputLayerNormWeightsPin
2307 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002308
2309 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2310 // Used to rescale normalized inputs to activation at forget gate.
2311 const ConstTensorPin forgetLayerNormWeightsPin =
2312 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2313 24,
2314 model,
2315 data,
2316 g_DontPermute,
2317 nullptr,
2318 true);
2319
2320 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2321 // Used to rescale normalized inputs to activation at cell gate.
2322 const ConstTensorPin cellLayerNormWeightsPin =
2323 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2324 25,
2325 model,
2326 data,
2327 g_DontPermute,
2328 nullptr,
2329 true);
2330
2331 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2332 // Used to rescale normalized inputs to activation at output gate.
2333 const ConstTensorPin outputLayerNormWeightsPin =
2334 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2335 26,
2336 model,
2337 data,
2338 g_DontPermute,
2339 nullptr,
2340 true);
2341
2342 // Outputs:
2343 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2344 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2345 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2346 if (!scratchBuffer)
2347 {
2348 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2349 }
2350 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2351 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2352 if (!outputStateOut)
2353 {
2354 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2355 }
2356 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2357 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2358 if (!cellStateOut)
2359 {
2360 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2361 }
2362 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2363 // effectively the same as the current “output state (out)” value.
2364 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2365 if (!output)
2366 {
2367 return Fail("%s: Could not read output 3: output", __func__);
2368 }
2369
2370 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002371 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002372 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2373 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2374 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2375 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2376 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2377 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2378 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2379 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2380 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2381 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2382 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2383 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2384 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2385 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2386 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2387 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2388 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2389 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2390 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2391 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2392 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2393
2394 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002395 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002396 desc.m_ActivationFunc = activation;
2397 desc.m_ClippingThresCell = cellClip;
2398 desc.m_ClippingThresProj = projClip;
2399 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2400 params.m_RecurrentToInputWeights == nullptr ||
2401 params.m_InputGateBias == nullptr);
2402 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2403 params.m_CellToOutputWeights != nullptr);
2404 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2405 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2406 params.m_ForgetLayerNormWeights != nullptr ||
2407 params.m_CellLayerNormWeights != nullptr ||
2408 params.m_OutputLayerNormWeights != nullptr);
2409
2410 // validate the optional input groups
2411 if (desc.m_CifgEnabled &&
2412 (params.m_InputToInputWeights != nullptr ||
2413 params.m_RecurrentToInputWeights != nullptr ||
2414 params.m_InputGateBias != nullptr))
2415 {
2416 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2417 " and input gate bias must be provided", __func__);
2418 }
2419
2420 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2421 {
2422 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2423 }
2424
2425 if (desc.m_PeepholeEnabled &&
2426 (params.m_CellToForgetWeights == nullptr ||
2427 params.m_CellToOutputWeights == nullptr ||
2428 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2429 {
2430 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2431 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2432 }
2433
2434 if (desc.m_LayerNormEnabled &&
2435 (params.m_ForgetLayerNormWeights == nullptr ||
2436 params.m_CellLayerNormWeights == nullptr ||
2437 params.m_OutputLayerNormWeights == nullptr ||
2438 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2439 {
2440 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2441 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2442 }
2443
2444 // Check if the layer is supported
2445 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002446 const TensorInfo& inputInfo = input.GetTensorInfo();
2447 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2448 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002449
2450 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002451 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2452 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2453 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2454 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002455
Pablo Tello972603f2019-11-28 15:21:41 +00002456 // Check if the scratch buffer shape was initialized,
2457 // In some cases the shape could be (0,0) which requires the driver
2458 // to infer the shape and set it up accordingly.
2459 // The code below does that.
2460 TensorInfo fixSbInfo = scratchBufferInfo;
2461 if (IsDynamicTensor(scratchBufferInfo))
2462 {
2463 auto & s = fixSbInfo.GetShape();
2464 s[0] = outputStateInInfo.GetShape()[0];
2465 if (desc.m_CifgEnabled)
2466 {
2467 // 2D tensor with dimensions [num_units * 3, batch_size] with CIFG
2468 s[1] = cellStateOutInfo.GetShape()[1]*3;
2469 }
2470 else
2471 {
2472 // scratch_buffer [num_units * 4, batch_size] without CIFG
2473 s[1] = cellStateOutInfo.GetShape()[1]*4;
2474 }
2475 }
2476
2477 if (IsDynamicTensor(outputStateOutInfo) ||
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002478 IsDynamicTensor(cellStateOutInfo) ||
2479 IsDynamicTensor(outputInfo))
2480 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002481 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2482 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2483 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002484 }
2485
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002486 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002487 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002488 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2489 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2490 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2491 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2492 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2493 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2494 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2495 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2496 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2497
2498 // Optional parameters
2499 if(!desc.m_CifgEnabled)
2500 {
2501 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2502 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2503 if (params.m_CellToInputWeights != nullptr)
2504 {
2505 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2506 }
2507 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2508 }
2509
2510 if(desc.m_ProjectionEnabled)
2511 {
2512 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2513 if (params.m_ProjectionBias != nullptr)
2514 {
2515 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2516 }
2517 }
2518
2519 if(desc.m_PeepholeEnabled)
2520 {
2521 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2522 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2523 }
2524
2525 if (desc.m_LayerNormEnabled)
2526 {
2527 if(!desc.m_CifgEnabled)
2528 {
2529 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2530 }
2531 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2532 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2533 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2534 }
2535
2536 bool isSupported = false;
2537 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2538 IsLstmSupported,
2539 data.m_Backends,
2540 isSupported,
2541 inputInfo,
2542 outputStateInInfo,
2543 cellStateInInfo,
Pablo Tello972603f2019-11-28 15:21:41 +00002544 fixSbInfo,
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002545 outputStateOutInfo,
2546 cellStateOutInfo,
2547 outputInfo,
2548 desc,
2549 paramsInfo);
2550 if (!isSupported)
2551 {
2552 return false;
2553 }
2554
2555 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002556 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002557
2558 input.Connect(layer->GetInputSlot(0));
2559 outputStateIn.Connect(layer->GetInputSlot(1));
2560 cellStateIn.Connect(layer->GetInputSlot(2));
2561
Pablo Tello972603f2019-11-28 15:21:41 +00002562
2563 return (
2564 (IsDynamicTensor(scratchBufferInfo)?
2565 SetupAndTrackLayerOutputSlotAndOverrideTensorInfo<hal_1_2::HalPolicy>(
2566 operation, 0, *layer, 0, model, data,fixSbInfo):
2567 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(
2568 operation, 0, *layer, 0, model, data)) &&
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002569 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2570 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2571 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2572}
2573
Sadik Armagan701d9a02019-09-04 15:16:18 +01002574bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2575{
2576 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002577 ActivationDescriptor desc;
2578 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002579
2580 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2581}
2582
Mike Kelly46272802019-08-14 17:00:48 +01002583bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2584{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002585 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002586 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2587}
2588
2589bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2590{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002591 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002592 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2593}
2594
2595bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2596{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002597 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002598 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2599}
2600
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002601bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002602{
2603 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2604
2605 if (!input.IsValid())
2606 {
2607 return Fail("%s: Operation has invalid inputs", __func__);
2608 }
2609
2610 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2611
2612 if (!output)
2613 {
2614 return Fail("%s: Could not read output 0", __func__);
2615 }
2616
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002617 const TensorInfo& inputInfo = input.GetTensorInfo();
2618 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002619 if (IsDynamicTensor(outputInfo))
2620 {
2621 return Fail("%s: Dynamic output tensors are not supported", __func__);
2622 }
2623
2624 // ArmNN does not currently support non-fixed weights or bias
2625 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2626 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2627
2628 if (weightsOperand == nullptr)
2629 {
2630 return Fail("%s: Operand is invalid", __func__);
2631 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002632 TransposeConvolution2dDescriptor desc;
2633 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002634
2635 // Determine whether padding is implicit or explicit
2636 bool implicitPadding = operation.inputs.size() == 9;
2637
2638 if (implicitPadding )
2639 {
2640 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2641 }
2642 else
2643 {
2644 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2645 }
2646
2647 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2648 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2649 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2650
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002651 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002652
2653 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2654 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002655 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002656 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2657 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2658
2659 // Bias is a 1D tensor
2660 const ConstTensorPin biasPin =
2661 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2662
2663 if (!weightsPin.IsValid())
2664 {
2665 return Fail("%s: Operation has invalid weights", __func__);
2666 }
2667
2668 if (!biasPin.IsValid())
2669 {
2670 return Fail("%s: Operation has invalid biases", __func__);
2671 }
2672
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002673 ConstTensor weights = weightsPin.GetConstTensor();
2674 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002675 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2676
2677 ActivationFn activation;
2678
2679 if (implicitPadding)
2680 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002681 int32_t strideX{0};
2682 int32_t strideY{0};
2683 int32_t padLeft{0};
2684 int32_t padRight{0};
2685 int32_t padTop{0};
2686 int32_t padBottom{0};
2687
David Monahan613b49c2019-06-27 11:37:47 +01002688 android::nn::PaddingScheme paddingScheme;
2689 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002690 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2691 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002692 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2693 {
2694 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2695 }
2696
2697 const uint32_t kernelX = weights.GetShape()[widthIndex];
2698 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002699 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2700 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002701
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002702 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2703 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2704
2705 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2706 // but Arm NN only supports values >= 0
2707 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2708 {
2709 return Fail("%s: Negative padding values are not supported", __func__);
2710 }
2711
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002712 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2713 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002714 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2715 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2716 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2717 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002718 }
2719 else if (operation.inputs.size() == 11)
2720 {
2721 // explicit padding
2722 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2723 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2724 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2725 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2726 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2727 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2728 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2729 {
2730 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2731 }
2732 }
2733 else
2734 {
2735 return Fail("%s: Unsupported number of operation inputs", __func__);
2736 }
2737
2738 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002739 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002740
2741 bool isSupported = false;
2742 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2743 IsTransposeConvolution2dSupported,
2744 data.m_Backends,
2745 isSupported,
2746 inputInfo,
2747 outputInfo,
2748 desc,
2749 weights.GetInfo(),
2750 biases);
2751 if (!isSupported)
2752 {
2753 return false;
2754 }
2755
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002756 IConnectableLayer* startLayer =
2757 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002758 if (!startLayer)
2759 {
2760 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2761 }
2762
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002763 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002764 if (!endLayer)
2765 {
2766 return Fail("%s: ProcessActivation failed", __func__);
2767 }
2768
2769 input.Connect(startLayer->GetInputSlot(0));
2770
2771 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2772}
2773
Mike Kellyb5fdf382019-06-11 16:35:25 +01002774} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002775} // namespace armnn_driver