blob: 8e4ef8a84686c413933832bbcb6fcd028dd8a23f [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(),
Kevin Mayaed08ac2019-12-12 16:33:31 +0000752 outputInfo,
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100753 reshapeDescriptor);
754
755 if (!isSupported)
756 {
757 return false;
758 }
759
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100760 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100761 assert(layer != nullptr);
762 input.Connect(layer->GetInputSlot(0));
763
764 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
765}
766
Mike Kelly46272802019-08-14 17:00:48 +0100767bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
768{
769 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
770 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
771}
772
773bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
774{
775 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
776 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
777}
778
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100779bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
780{
781 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
782
783 //
784 // Parse data
785 //
786 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
787 if (!input.IsValid())
788 {
789 return Fail("%s: Operation has invalid inputs", __func__);
790 }
791 const TensorInfo& inputInfo = input.GetTensorInfo();
792
793 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
794 if (!output)
795 {
796 return Fail("%s: Could not read output 0", __func__);
797 }
798 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
799 if (IsDynamicTensor(outputInfo))
800 {
801 return Fail("%s: Dynamic output tensors are not supported", __func__);
802 }
803
804 // Look ahead to determine data layout
805 DataLayout dataLayout = DataLayout::NHWC;
806 if (operation.inputs.size() == 12)
807 {
808 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
809 }
810 else
811 {
812 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
813 }
814
815 // NOTE:
816 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
817 // but Arm NN expects the filter's height and width indices to match the input's height and
818 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
819 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
820 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
821 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
822 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
823 const ConstTensorPin biasesPin =
824 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
825 if (!weightsPin.IsValid() || !biasesPin.IsValid())
826 {
827 return Fail("%s: Operation has invalid inputs", __func__);
828 }
829
830 ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000831 ConstTensor biases = biasesPin.GetConstTensor();
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100832 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
833
834 const TensorShape& inputShape = inputInfo.GetShape();
835 const TensorShape& outputShape = outputInfo.GetShape();
836 const TensorShape& weightsShape = weights.GetShape();
837 const TensorShape& biasesShape = biases.GetShape();
838
839 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
840 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
841 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
842 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
843
844 Convolution2dDescriptor desc;
845 desc.m_DataLayout = dataLayout;
846 desc.m_BiasEnabled = true;
847
848 int numGroups;
849 ActivationFn activation;
850
851 if (operation.inputs.size() == 12)
852 {
853 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
854 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
855 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
856 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
857 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
858 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
859 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
860 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
861 {
862 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
863 }
864
865 }
866 else if (operation.inputs.size() == 9)
867 {
868 android::nn::PaddingScheme paddingScheme;
869 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
870 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
871 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
872 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
873 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
874 {
875 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
876 }
877
878 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
879 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
880
881 const uint32_t kernelX = weightsShape[widthIndex];
882 const uint32_t kernelY = weightsShape[heightIndex];
883
884 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
885 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
886 }
887 else
888 {
889 return Fail("%s: Unsupported number of operation inputs", __func__);
890 }
891
892 const unsigned int outputChannels = outputShape[channelsIndex];
893
894 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
895 const unsigned int channelMultiplier = outputChannels / numGroups;
896
897 //
898 // Validate all relevant inputs
899 //
900 if (numGroups <= 0)
901 {
902 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
903 }
904
905 if (outputChannels % numGroups != 0u)
906 {
907 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
908 }
909
910 //
911 // Set up Splitter layer
912 //
913 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
914 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
915
916 TensorInfo splitterOutputInfo(4,
917 splitterDimSizes,
918 inputInfo.GetDataType(),
919 inputInfo.GetQuantizationScale(),
920 inputInfo.GetQuantizationOffset());
921
922 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
923
924 ViewsDescriptor splitterDesc(numGroups);
925 for (unsigned int group = 0u; group < numGroups; ++group)
926 {
927 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
928 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
929 {
930 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
931 }
932 }
933
934 bool isSupported = false;
935 FORWARD_LAYER_SUPPORT_FUNC(__func__,
936 IsSplitterSupported,
937 data.m_Backends,
938 isSupported,
939 inputInfo,
940 splitterOutputInfos,
941 splitterDesc);
942 if (!isSupported)
943 {
944 return false;
945 }
946
947 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
948 if (!splitterLayer)
949 {
950 return Fail("%s: Failed to add SplitterLayer", __func__);
951 }
952
953 input.Connect(splitterLayer->GetInputSlot(0));
954 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
955 {
956 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
957 }
958
959 //
960 // Set up Convolution2d layers for each group
961 //
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000962
963 // Set up group tensor shapes
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100964 TensorShape groupInputShape(inputShape);
965 groupInputShape[channelsIndex] = channelsPerGroup;
966
967 TensorShape groupOutputShape(outputShape);
968 groupOutputShape[channelsIndex] = 1;
969
970 TensorShape groupWeightsShape(weightsShape);
971 groupWeightsShape[0] /= channelMultiplier * numGroups;
972
973 TensorShape groupBiasesShape({ 1 });
974
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000975 // Set up group tensor infos
976 TensorInfo groupInputInfo(inputInfo);
977 groupInputInfo.SetShape(groupInputShape);
978
979 const TensorInfo& weightsInfo = weights.GetInfo();
980 TensorInfo groupWeightsInfo(weightsInfo);
981 groupWeightsInfo.SetShape(groupWeightsShape);
982
983 const TensorInfo& biasesInfo = biases.GetInfo();
984 TensorInfo groupBiasesInfo(biasesInfo);
985 groupBiasesInfo.SetShape(groupBiasesShape);
986
987 TensorInfo groupOutputInfo(outputInfo);
988 groupOutputInfo.SetShape(groupOutputShape);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100989
990 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
991 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
992
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000993 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100994 for (unsigned int group = 0u; group < numGroups; ++group)
995 {
996 for (unsigned int m = 0u; m < channelMultiplier; ++m)
997 {
998 auto index = group * channelMultiplier + m;
999
1000 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1001 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1002
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +00001003 if (weightsInfo.HasPerAxisQuantization())
1004 {
1005 // Extract per-axis quantization scales for group weights
1006 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1007 groupWeightsInfo.SetQuantizationScales(
1008 std::vector<float>(weightsQuantScales.begin() + index,
1009 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1010
1011 // Extract per-axis quantization scales for group biases
1012 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1013 groupBiasesInfo.SetQuantizationScales(
1014 std::vector<float>(biasesQuantScales.begin() + index,
1015 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1016 }
1017
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001018 // Extract weights and biases data for current group convolution
1019 ConstTensor groupWeights(groupWeightsInfo,
1020 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1021 weightsDataOffset));
1022 ConstTensor groupBiases(groupBiasesInfo,
1023 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1024 biasesDataOffset));
1025
1026 isSupported = false;
1027 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1028 IsConvolution2dSupported,
1029 data.m_Backends,
1030 isSupported,
1031 groupInputInfo,
1032 groupOutputInfo,
1033 desc,
1034 groupWeightsInfo,
1035 Optional<TensorInfo>(groupBiasesInfo));
1036 if (!isSupported)
1037 {
1038 return false;
1039 }
1040
1041 IConnectableLayer *convLayer =
1042 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1043 if (!convLayer)
1044 {
1045 return Fail("%s: AddConvolution2dLayer failed", __func__);
1046 }
1047
1048 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1049 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1050
1051 convLayers[index] = convLayer;
1052 }
1053 }
1054
1055 //
1056 // Set up Concat layer
1057 //
1058 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
1059 for (unsigned int group = 0u; group < numGroups; ++group)
1060 {
1061 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1062 {
1063 auto index = group * channelMultiplier + m;
1064 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1065 concatDescriptor.SetConcatAxis(channelsIndex);
1066 }
1067 }
1068
1069 isSupported = false;
1070 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1071 IsConcatSupported,
1072 data.m_Backends,
1073 isSupported,
1074 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1075 outputInfo,
1076 concatDescriptor);
1077 if (!isSupported)
1078 {
1079 return false;
1080 }
1081
1082 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1083 if (!concatLayer)
1084 {
1085 return Fail("%s: AddConcatLayer failed", __func__);
1086 }
1087
1088 for (unsigned int group = 0u; group < numGroups; ++group)
1089 {
1090 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1091 {
1092 auto index = group * channelMultiplier + m;
1093 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1094 }
1095 }
1096 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1097
1098 //
1099 // Set up Activation layer (if it is set)
1100 //
1101 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
1102 if (!endLayer)
1103 {
1104 return Fail("%s: ProcessActivation failed", __func__);
1105 }
1106
1107 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1108}
1109
Aron Virginas-Tara2a73802019-10-09 15:30:40 +01001110bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
1111{
1112 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
1113
1114 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1115 if (!input.IsValid())
1116 {
1117 return Fail("%s: Operation has an invalid input 0", __func__);
1118 }
1119
1120 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1121 if (!output)
1122 {
1123 return Fail("%s: Operation has an invalid output", __func__);
1124 }
1125
1126 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1127 if (IsDynamicTensor(outputInfo))
1128 {
1129 return Fail("%s: Dynamic output tensors are not supported", __func__);
1130 }
1131
1132 // Determine data type of input tensor
1133 OperandType inputType;
1134 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1135 {
1136 return Fail("%s: Operation has invalid inputs", __func__);
1137 }
1138
1139 InstanceNormalizationDescriptor desc;
1140
1141 // Read gamma, beta & epsilon
1142 if (inputType == OperandType::TENSOR_FLOAT16)
1143 {
1144 Half fp16Gamma;
1145 Half fp16Beta;
1146 Half fp16Epsilon;
1147
1148 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1149 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1150 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1151 {
1152 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1153 }
1154
1155 desc.m_Gamma = static_cast<float>(fp16Gamma);
1156 desc.m_Beta = static_cast<float>(fp16Beta);
1157 desc.m_Eps = static_cast<float>(fp16Epsilon);
1158 }
1159 else if (inputType == OperandType::TENSOR_FLOAT32)
1160 {
1161 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1162 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1163 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1164 {
1165 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1166 }
1167 }
1168 else
1169 {
1170 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1171 }
1172
1173 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1174
1175 bool isSupported = false;
1176 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1177 IsInstanceNormalizationSupported,
1178 data.m_Backends,
1179 isSupported,
1180 input.GetTensorInfo(),
1181 outputInfo,
1182 desc);
1183 if (!isSupported)
1184 {
1185 return false;
1186 }
1187
1188 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1189 input.Connect(layer->GetInputSlot(0));
1190
1191 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1192}
1193
Mike Kelly46272802019-08-14 17:00:48 +01001194bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1195{
1196 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1197 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1198}
1199
Sadik Armagan15d63e22019-07-26 16:59:35 +01001200bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1201{
1202 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001203 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001204}
1205
Mike Kelly46272802019-08-14 17:00:48 +01001206bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1207 const Model& model,
1208 ConversionData& data)
1209{
1210 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1211 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1212}
1213
1214bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1215{
1216 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1217 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1218}
1219
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001220bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1221{
1222 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1223
1224 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1225 if (!input.IsValid())
1226 {
1227 return Fail("%s: Failed to read input 0", __func__);
1228 }
1229
1230 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1231 if (!output)
1232 {
1233 return Fail("%s: Failed to read output", __func__);
1234 }
1235
1236 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1237 if (IsDynamicTensor(outputInfo))
1238 {
1239 return Fail("%s: Dynamic output tensors are not supported", __func__);
1240 }
1241
1242 // Determine data type of input tensor
1243 OperandType inputType;
1244 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1245 {
1246 return Fail("%s: Operation has invalid inputs", __func__);
1247 }
1248
1249 LogSoftmaxDescriptor descriptor;
1250
1251 // Read beta
1252 if (inputType == OperandType::TENSOR_FLOAT16)
1253 {
1254 Half fp16Beta;
1255 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1256 {
1257 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1258 }
1259
1260 descriptor.m_Beta = static_cast<float>(fp16Beta);
1261 }
1262 else if (inputType == OperandType::TENSOR_FLOAT32)
1263 {
1264 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1265 {
1266 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1267 }
1268 }
1269 else
1270 {
1271 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1272 }
1273
1274 // Read axis
1275 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1276 {
1277 return Fail("%s: Failed to read input 2", __func__);
1278 }
1279
1280 bool isSupported = false;
1281 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1282 IsLogSoftmaxSupported,
1283 data.m_Backends,
1284 isSupported,
1285 input.GetTensorInfo(),
1286 outputInfo,
1287 descriptor);
1288 if (!isSupported)
1289 {
1290 return false;
1291 }
1292
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001293 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001294 if (!layer)
1295 {
1296 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1297 }
1298
1299 input.Connect(layer->GetInputSlot(0));
1300
1301 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1302}
1303
Sadik Armagan15d63e22019-07-26 16:59:35 +01001304bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1305{
1306 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001307 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001308}
1309
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001310bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1311{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001312 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1313
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001314 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1315 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1316
1317 if (!input0.IsValid() || !input1.IsValid())
1318 {
1319 return Fail("%s: Operation has invalid inputs", __func__);
1320 }
1321
1322 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1323 if (!outputOperand)
1324 {
1325 return Fail("%s: Could not read output", __func__);
1326 }
1327
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001328 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001329 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001330 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001331 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001332 }
1333
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001334 bool isSupported = false;
1335 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1336 IsMaximumSupported,
1337 data.m_Backends,
1338 isSupported,
1339 input0.GetTensorInfo(),
1340 input1.GetTensorInfo(),
1341 outInfo);
1342
1343 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001344 {
1345 return false;
1346 }
1347
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001348 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001349 assert(layer != nullptr);
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001350 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001351 if (!isReshapeSupported)
1352 {
1353 return false;
1354 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001355
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001356 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001357}
1358
Mike Kelly46272802019-08-14 17:00:48 +01001359bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1360{
1361 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1362 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1363}
1364
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001365bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1366{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001367 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1368
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001369 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1370 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1371
1372 if (!input0.IsValid() || !input1.IsValid())
1373 {
1374 return Fail("%s: Operation has invalid inputs", __func__);
1375 }
1376
1377 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1378 if (!output)
1379 {
1380 return Fail("%s: Could not read output 0", __func__);
1381 }
1382
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001383 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001384 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001385 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001386 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001387 }
1388
1389 bool isSupported = false;
1390 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1391 IsMinimumSupported,
1392 data.m_Backends,
1393 isSupported,
1394 input0.GetTensorInfo(),
1395 input1.GetTensorInfo(),
1396 outputInfo);
1397
1398 if (!isSupported)
1399 {
1400 return false;
1401 }
1402
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001403 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001404 assert(layer != nullptr);
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001405 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001406 if (!isReshapeSupported)
1407 {
1408 return false;
1409 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001410
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001411 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001412}
1413
Mike Kelly46272802019-08-14 17:00:48 +01001414bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1415{
1416 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1417 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1418}
1419
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001420bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1421{
1422 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1423 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1424}
1425
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001426bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1427{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001428 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1429
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001430 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1431 if (!input.IsValid())
1432 {
1433 return Fail("%s: Could not read input 0", __func__);
1434 }
1435
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001436 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1437 if (!output)
1438 {
1439 return Fail("%s: Could not read output", __func__);
1440 }
1441
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001442 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001443 unsigned int rank = inputInfo.GetNumDimensions();
1444
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001445 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001446 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1447 {
1448 return Fail("%s: Could not convert paddings", __func__);
1449 }
1450
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001451 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001452 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001453 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001454 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001455 }
1456
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001457 // Determine type of padding value
1458 OperandType operandType0;
1459 OperandType operandType2;
1460
1461 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1462 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1463 {
1464 return Fail("%s: Operation has invalid inputs", __func__);
1465 }
1466
1467 // Read value to use for padding
1468 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1469 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001470 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001471 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1472 {
1473 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1474 }
1475
1476 descriptor.m_PadValue = f16PadValue;
1477 }
1478 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1479 {
1480 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1481 {
1482 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1483 }
1484 }
1485 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1486 {
Mike Kelly3c673942019-07-25 09:26:06 +01001487 int32_t intPadValue = 0;
1488 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001489 {
1490 return Fail("%s: Could not read input 2 (INT32)", __func__);
1491 }
Mike Kelly3c673942019-07-25 09:26:06 +01001492 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001493 }
1494 else
1495 {
1496 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1497 }
1498
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001499 bool isSupported = false;
1500 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1501 IsPadSupported,
1502 data.m_Backends,
1503 isSupported,
1504 inputInfo,
1505 outputInfo,
1506 descriptor);
1507 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001508 {
1509 return false;
1510 }
1511
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001512 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001513 assert(layer != nullptr);
1514 input.Connect(layer->GetInputSlot(0));
1515 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1516
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001517 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001518}
1519
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001520bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1521{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001522 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1523
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001524 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1525 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1526
1527 if (!input.IsValid() || !alpha.IsValid())
1528 {
1529 return Fail("%s: Operation has invalid inputs", __func__);
1530 }
1531
1532 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1533
1534 if (!output)
1535 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001536 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001537 }
1538
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001539 const TensorInfo& inputInfo = input.GetTensorInfo();
1540 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1541 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001542
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001543 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001544 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001545 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001546 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001547
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001548 bool isSupported = false;
1549 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1550 IsPreluSupported,
1551 data.m_Backends,
1552 isSupported,
1553 inputInfo,
1554 alphaInfo,
1555 outputInfo);
1556 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001557 {
1558 return false;
1559 }
1560
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001561 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001562
1563 if (!layer)
1564 {
1565 return Fail("%s: AddPreluLayer failed", __func__);
1566 }
1567
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001568 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001569 if (!isReshapeSupported)
1570 {
1571 return false;
1572 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001573
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001574 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001575}
1576
Sadik Armagan5a476a82019-07-30 09:43:18 +01001577bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1578{
1579 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1580
1581 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1582 if (!input.IsValid())
1583 {
1584 return Fail("%s: Operation has invalid input", __func__);
1585 }
1586
1587 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1588 if (!outputOperand)
1589 {
1590 return Fail("%s: Operation has invalid outputs", __func__);
1591 }
1592
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001593 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001594 if (IsDynamicTensor(outputInfo))
1595 {
1596 return Fail("%s: Dynamic output tensors are not supported", __func__);
1597 }
1598
1599 bool isSupported = false;
1600 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1601 IsQuantizeSupported,
1602 data.m_Backends,
1603 isSupported,
1604 input.GetTensorInfo(),
1605 outputInfo);
1606 if (!isSupported)
1607 {
1608 return false;
1609 }
1610
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001611 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001612 assert(layer != nullptr);
1613 input.Connect(layer->GetInputSlot(0));
1614
1615 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1616}
1617
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001618bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1619{
1620 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1621
1622 //Inputs:
1623 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1624 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1625 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1626 if (!input.IsValid())
1627 {
1628 return Fail("%s: Could not read input 0: input", __func__);
1629 }
1630
1631 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1632 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1633 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1634 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1635 if (!previousCellStateIn.IsValid())
1636 {
1637 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1638 }
1639
1640 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1641 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1642 // is quantized with a fixed quantization range of -1, 127/128.
1643 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1644 if (!previousOutputIn.IsValid())
1645 {
1646 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1647 }
1648
1649 // Get the input tensors:
1650 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1651 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1652 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1653 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001654 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001655
1656 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1657 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1658 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1659 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001660 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001661
1662 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1663 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1664 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1665 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001666 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001667
1668 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1669 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1670 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1671 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001672 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001673
1674 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1675 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1676 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1677 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001678 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001679
1680 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1681 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1682 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1683 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001684 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001685
1686 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1687 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1688 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1689 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001690 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001691
1692 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1693 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1694 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1695 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001696 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001697
1698 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1699 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1700 // of input and weights scales and zeroPoint equal to 0.
1701 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001702 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001703
1704 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1705 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1706 // of input and weights scales and zeroPoint equal to 0.
1707 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001708 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001709
1710 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1711 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1712 // and weights scales and zeroPoint equal to 0.
1713 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001714 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001715
1716 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1717 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1718 // of input and weights scales and zeroPoint equal to 0.
1719 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001720 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001721
1722 if (!inputToInputWeightsPin.IsValid() ||
1723 !inputToForgetWeightsPin.IsValid() ||
1724 !inputToCellWeightsPin.IsValid() ||
1725 !inputToOutputWeightsPin.IsValid() ||
1726 !recurrentToInputWeightsPin.IsValid() ||
1727 !recurrentToForgetWeightsPin.IsValid() ||
1728 !recurrentToCellWeightsPin.IsValid() ||
1729 !recurrentToOutputWeightsPin.IsValid() ||
1730 !inputGateBiasPin.IsValid() ||
1731 !forgetGateBiasPin.IsValid() ||
1732 !cellBiasPin.IsValid() ||
1733 !outputGateBiasPin.IsValid())
1734 {
1735 return Fail("%s: Operation has invalid tensor inputs", __func__);
1736 }
1737
1738 // Outputs:
1739 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1740 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1741 // of -2^4, 2^4 * 32767/32768.
1742 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1743 if (!cellStateOut)
1744 {
1745 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1746 }
1747
1748 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1749 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1750 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1751 if (!output)
1752 {
1753 return Fail("%s: Could not read output 1: output", __func__);
1754 }
1755
1756 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001757 const TensorInfo& inputInfo = input.GetTensorInfo();
1758 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1759 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001760
1761 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001762 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1763 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001764
1765 // Dynamic tensors currently not supported
1766 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1767 {
1768 return Fail("%s: Dynamic output tensors are not supported", __func__);
1769 }
1770
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001771 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001772
1773 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1774 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1775 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1776 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1777 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1778 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1779 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1780 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1781 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1782 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1783 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1784 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1785
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001786 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001787 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1788 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1789 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1790 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1791 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1792 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1793 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1794 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1795 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1796 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1797 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1798 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1799
1800 bool isSupported = false;
1801 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1802 IsQuantizedLstmSupported,
1803 data.m_Backends,
1804 isSupported,
1805 inputInfo,
1806 previousCellStateInInfo,
1807 previousOutputInInfo,
1808 cellStateOutInfo,
1809 outputInfo,
1810 paramsInfo);
1811
1812 if (!isSupported)
1813 {
1814 return false;
1815 }
1816
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001817 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001818 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001819 previousCellStateIn.Connect(layer->GetInputSlot(1));
1820 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001821
1822 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1823 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1824}
1825
Sadik Armagan61113162019-07-25 09:09:40 +01001826bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1827{
1828 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1829 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1830}
1831
1832bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1833{
1834 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1835 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1836}
1837
1838bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1839{
1840 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1841 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1842}
1843
Mike Kelly46272802019-08-14 17:00:48 +01001844bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1845{
1846 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1847 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1848}
1849
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001850bool HalPolicy::ConvertResize(const Operation& operation,
1851 const Model& model,
1852 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001853 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001854{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001855 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001856 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001857
1858 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001859 if (!input.IsValid())
1860 {
1861 return Fail("%s: Could not read input 0", __func__);
1862 }
1863
1864 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1865 if (!output)
1866 {
1867 return Fail("%s: Could not read output 0", __func__);
1868 }
1869
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001870 const TensorInfo& inputInfo = input.GetTensorInfo();
1871 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001872
1873 if (IsDynamicTensor(outputInfo))
1874 {
1875 return Fail("%s: Dynamic output tensors are not supported", __func__);
1876 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001877
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001878 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001879 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001880 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1881
1882 OperandType operandType1;
1883 OperandType operandType2;
1884
1885 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1886 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1887 {
1888 return Fail("%s: Operation has invalid inputs", __func__);
1889 }
1890
1891 if (operandType1 != operandType2)
1892 {
1893 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1894 }
1895
1896 if (operandType1 == OperandType::INT32)
1897 {
1898 // Case 1: resizing by shape
1899 int32_t targetWidth = 0;
1900 int32_t targetHeight = 0;
1901
1902 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1903 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1904 {
1905 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1906 }
1907
1908 if (targetWidth < 0 || targetHeight < 0)
1909 {
1910 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1911 "Target width/height cannot be < 0", __func__);
1912 }
1913
1914 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001915 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001916 }
1917 else if (operandType1 == OperandType::FLOAT32)
1918 {
1919 // Case 2: resizing by scale
1920 float widthScale = 1.0f;
1921 float heightScale = 1.0f;
1922
1923 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1924 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1925 {
1926 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1927 }
1928
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001929 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001930 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1931
1932 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1933 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1934
1935 descriptor.m_TargetWidth = std::floor(width * widthScale);
1936 descriptor.m_TargetHeight = std::floor(height * heightScale);
1937 }
Keith Davisd410b602019-12-19 12:31:30 +00001938 else if (operandType1 == OperandType::FLOAT16)
1939 {
1940 Half widthScale;
1941 Half heightScale;
1942
1943 if (!GetInputScalar<HalPolicy>(operation, 1, HalPolicy::OperandType::FLOAT16, widthScale, model, data) ||
1944 !GetInputScalar<HalPolicy>(operation, 2, HalPolicy::OperandType::FLOAT16, heightScale, model, data))
1945 {
1946 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1947 }
1948
1949 const TensorShape& inputShape = inputInfo.GetShape();
1950 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1951
1952 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
1953 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
1954
1955 descriptor.m_TargetWidth = std::floor(width * widthScale);
1956 descriptor.m_TargetHeight = std::floor(height * heightScale);
1957 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001958 else
1959 {
Keith Davisd410b602019-12-19 12:31:30 +00001960 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001961 }
1962
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001963 bool isSupported = false;
1964 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1965 IsResizeSupported,
1966 data.m_Backends,
1967 isSupported,
1968 inputInfo,
1969 outputInfo,
1970 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001971
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001972 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001973 {
1974 return false;
1975 }
1976
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001977 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001978
1979 assert(layer != nullptr);
1980
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001981 input.Connect(layer->GetInputSlot(0));
1982
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001983 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001984}
1985
Finn Williamsd74c5052019-07-30 17:06:00 +01001986bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1987{
1988 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1989 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1990}
1991
Keith Davisa6bc52f2019-06-26 09:39:49 +01001992bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1993{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001994 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001995
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001996 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001997 if (!input.IsValid() )
1998 {
1999 return Fail("%s: Operation has invalid inputs", __func__);
2000 }
2001
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002002 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01002003 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01002004 if (rank != 4)
2005 {
2006 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2007 }
2008
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002009 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2010 if (!output)
2011 {
2012 return Fail("%s: Could not read output 0", __func__);
2013 }
2014
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002015 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002016 if (IsDynamicTensor(outputInfo))
2017 {
2018 return Fail("%s: Dynamic output tensors are not supported", __func__);
2019 }
2020
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002021 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01002022
2023 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
2024
2025 if (desc.m_BlockSize <= 1)
2026 {
2027 return Fail("%s: Block size must be at least 1 in all dimensions");
2028 }
2029
2030 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
2031
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002032 bool isSupported = false;
2033 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2034 IsSpaceToDepthSupported,
2035 data.m_Backends,
2036 isSupported,
2037 inputInfo,
2038 outputInfo,
2039 desc);
2040 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01002041 {
2042 return false;
2043 }
2044
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002045 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002046 assert(layer != nullptr);
2047 input.Connect(layer->GetInputSlot(0));
2048
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002049 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002050}
2051
Francis Murtagh074c25a2019-07-22 16:40:57 +01002052bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
2053{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002054 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
2055
Francis Murtagh074c25a2019-07-22 16:40:57 +01002056 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2057 if (!input.IsValid())
2058 {
2059 return Fail("%s: Operation has invalid inputs", __func__);
2060 }
2061
2062 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2063 if (!outputOperand)
2064 {
2065 return Fail("%s: Operation has no outputs", __func__);
2066 }
2067
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002068 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01002069 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01002070 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002071 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002072 }
2073
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002074 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01002075 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
2076 {
2077 return Fail("%s: Operation has invalid inputs", __func__);
2078 }
2079
2080 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
2081 2,
2082 HalPolicy::OperandType::INT32,
2083 desc.m_Axis,
2084 model,
2085 data))
2086 {
2087 return Fail("%s: Operation has invalid inputs", __func__);
2088 }
2089
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01002090 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2091 !(desc.m_Axis == 1 ||
2092 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2093 {
2094 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2095 }
2096
Francis Murtagh074c25a2019-07-22 16:40:57 +01002097 bool isSupported = false;
2098 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2099 IsSoftmaxSupported,
2100 data.m_Backends,
2101 isSupported,
2102 input.GetTensorInfo(),
2103 outputInfo,
2104 desc);
2105 if (!isSupported)
2106 {
2107 return false;
2108 }
2109
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002110 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002111 assert(layer != nullptr);
2112 input.Connect(layer->GetInputSlot(0));
2113
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002114 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002115}
2116
Mike Kelly0a879362019-07-29 16:56:31 +01002117bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2118{
2119 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2120 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2121}
2122
Sadik Armagan61113162019-07-25 09:09:40 +01002123bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2124{
2125 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2126 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2127}
2128
Pablo Tello972603f2019-11-28 15:21:41 +00002129template<typename HalPolicy,
2130 typename HalOperation = typename HalPolicy::Operation,
2131 typename HalModel = typename HalPolicy::Model>
2132bool SetupAndTrackLayerOutputSlotAndOverrideTensorInfo(const HalOperation& operation,
2133 uint32_t operationOutputIndex,
2134 armnn::IConnectableLayer& layer,
2135 uint32_t layerOutputIndex,
2136 const HalModel& model,
2137 ConversionData& data,
2138 const armnn::TensorInfo tensor_info)
2139{
2140 using HalOperand = typename HalPolicy::Operand;
2141
2142 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
2143 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
2144 {
2145 return false;
2146 }
2147
2148 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
2149
2150 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
2151 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
2152
2153 outputSlot.SetTensorInfo(tensor_info);
2154
2155 return true;
2156}
2157
2158
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002159bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2160{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002161 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2162
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002163 // Inputs:
2164 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2165 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2166 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2167 if (!input.IsValid())
2168 {
2169 return Fail("%s: Could not read input 0: input", __func__);
2170 }
2171 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2172 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2173 if (!outputStateIn.IsValid())
2174 {
2175 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2176 }
2177 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2178 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2179 if (!cellStateIn.IsValid())
2180 {
2181 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2182 }
2183
2184 // Get the mandatory input tensors:
2185 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2186 // [num_units, input_size].
2187 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002188 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002189 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2190 // [num_units, input_size].
2191 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002192 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002193 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2194 // [num_units, input_size].
2195 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002196 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002197 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2198 // [num_units, output_size].
2199 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002200 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002201 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2202 // [num_units, output_size].
2203 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002204 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002205 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2206 // [num_units, output_size].
2207 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002208 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002209 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2210 const ConstTensorPin forgetGateBiasPin =
2211 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2212 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2213 const ConstTensorPin cellBiasPin =
2214 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2215 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2216 const ConstTensorPin outputGateBiasPin =
2217 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2218
2219 if (!inputToForgetWeightsPin.IsValid() ||
2220 !inputToCellWeightsPin.IsValid() ||
2221 !inputToOutputWeightsPin.IsValid() ||
2222 !recurrentToForgetWeightsPin.IsValid() ||
2223 !recurrentToCellWeightsPin.IsValid() ||
2224 !recurrentToOutputWeightsPin.IsValid() ||
2225 !forgetGateBiasPin.IsValid() ||
2226 !cellBiasPin.IsValid() ||
2227 !outputGateBiasPin.IsValid())
2228 {
2229 return Fail("%s: Operation has invalid tensor inputs", __func__);
2230 }
2231
2232 // Get the optional input tensors:
2233 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2234 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2235 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002236 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002237 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2238 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2239 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2240 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002241 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002242 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2243 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002244 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002245 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2246 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002247 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002248 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2249 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002250 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002251 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2252 const ConstTensorPin inputGateBiasPin =
2253 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2254 12,
2255 model,
2256 data,
2257 g_DontPermute,
2258 nullptr,
2259 true);
2260
2261 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2262 // [output_size, num_units].
2263 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002264 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002265 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2266 const ConstTensorPin projectionBiasPin =
2267 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2268 17,
2269 model,
2270 data,
2271 g_DontPermute,
2272 nullptr,
2273 true);
2274
2275 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2276 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2277 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2278 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2279 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2280 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2281 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2282 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2283 {
2284 return Fail("%s: Operation has invalid tensor inputs", __func__);
2285 }
2286
2287 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2288 // 20: The activation function: A value indicating the activation function:
2289 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2290 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2291 // If set to 0.0 then clipping is disabled.
2292 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2293 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2294 ActivationFn activation;
2295 float cellClip;
2296 float projClip;
2297 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2298 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2299 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2300 {
2301 return Fail("%s: Operation has invalid scalar inputs", __func__);
2302 }
2303
2304 // Get the normalization tensors
2305 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2306 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002307 const ConstTensorPin inputLayerNormWeightsPin
2308 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002309
2310 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2311 // Used to rescale normalized inputs to activation at forget gate.
2312 const ConstTensorPin forgetLayerNormWeightsPin =
2313 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2314 24,
2315 model,
2316 data,
2317 g_DontPermute,
2318 nullptr,
2319 true);
2320
2321 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2322 // Used to rescale normalized inputs to activation at cell gate.
2323 const ConstTensorPin cellLayerNormWeightsPin =
2324 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2325 25,
2326 model,
2327 data,
2328 g_DontPermute,
2329 nullptr,
2330 true);
2331
2332 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2333 // Used to rescale normalized inputs to activation at output gate.
2334 const ConstTensorPin outputLayerNormWeightsPin =
2335 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2336 26,
2337 model,
2338 data,
2339 g_DontPermute,
2340 nullptr,
2341 true);
2342
2343 // Outputs:
2344 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2345 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2346 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2347 if (!scratchBuffer)
2348 {
2349 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2350 }
2351 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2352 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2353 if (!outputStateOut)
2354 {
2355 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2356 }
2357 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2358 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2359 if (!cellStateOut)
2360 {
2361 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2362 }
2363 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2364 // effectively the same as the current “output state (out)” value.
2365 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2366 if (!output)
2367 {
2368 return Fail("%s: Could not read output 3: output", __func__);
2369 }
2370
2371 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002372 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002373 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2374 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2375 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2376 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2377 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2378 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2379 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2380 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2381 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2382 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2383 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2384 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2385 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2386 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2387 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2388 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2389 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2390 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2391 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2392 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2393 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2394
2395 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002396 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002397 desc.m_ActivationFunc = activation;
2398 desc.m_ClippingThresCell = cellClip;
2399 desc.m_ClippingThresProj = projClip;
2400 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2401 params.m_RecurrentToInputWeights == nullptr ||
2402 params.m_InputGateBias == nullptr);
2403 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2404 params.m_CellToOutputWeights != nullptr);
2405 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2406 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2407 params.m_ForgetLayerNormWeights != nullptr ||
2408 params.m_CellLayerNormWeights != nullptr ||
2409 params.m_OutputLayerNormWeights != nullptr);
2410
2411 // validate the optional input groups
2412 if (desc.m_CifgEnabled &&
2413 (params.m_InputToInputWeights != nullptr ||
2414 params.m_RecurrentToInputWeights != nullptr ||
2415 params.m_InputGateBias != nullptr))
2416 {
2417 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2418 " and input gate bias must be provided", __func__);
2419 }
2420
2421 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2422 {
2423 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2424 }
2425
2426 if (desc.m_PeepholeEnabled &&
2427 (params.m_CellToForgetWeights == nullptr ||
2428 params.m_CellToOutputWeights == nullptr ||
2429 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2430 {
2431 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2432 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2433 }
2434
2435 if (desc.m_LayerNormEnabled &&
2436 (params.m_ForgetLayerNormWeights == nullptr ||
2437 params.m_CellLayerNormWeights == nullptr ||
2438 params.m_OutputLayerNormWeights == nullptr ||
2439 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2440 {
2441 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2442 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2443 }
2444
2445 // Check if the layer is supported
2446 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002447 const TensorInfo& inputInfo = input.GetTensorInfo();
2448 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2449 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002450
2451 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002452 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2453 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2454 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2455 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002456
Pablo Tello972603f2019-11-28 15:21:41 +00002457 // Check if the scratch buffer shape was initialized,
2458 // In some cases the shape could be (0,0) which requires the driver
2459 // to infer the shape and set it up accordingly.
2460 // The code below does that.
2461 TensorInfo fixSbInfo = scratchBufferInfo;
2462 if (IsDynamicTensor(scratchBufferInfo))
2463 {
2464 auto & s = fixSbInfo.GetShape();
2465 s[0] = outputStateInInfo.GetShape()[0];
2466 if (desc.m_CifgEnabled)
2467 {
2468 // 2D tensor with dimensions [num_units * 3, batch_size] with CIFG
2469 s[1] = cellStateOutInfo.GetShape()[1]*3;
2470 }
2471 else
2472 {
2473 // scratch_buffer [num_units * 4, batch_size] without CIFG
2474 s[1] = cellStateOutInfo.GetShape()[1]*4;
2475 }
2476 }
2477
2478 if (IsDynamicTensor(outputStateOutInfo) ||
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002479 IsDynamicTensor(cellStateOutInfo) ||
2480 IsDynamicTensor(outputInfo))
2481 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002482 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2483 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2484 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002485 }
2486
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002487 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002488 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002489 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2490 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2491 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2492 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2493 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2494 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2495 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2496 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2497 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2498
2499 // Optional parameters
2500 if(!desc.m_CifgEnabled)
2501 {
2502 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2503 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2504 if (params.m_CellToInputWeights != nullptr)
2505 {
2506 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2507 }
2508 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2509 }
2510
2511 if(desc.m_ProjectionEnabled)
2512 {
2513 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2514 if (params.m_ProjectionBias != nullptr)
2515 {
2516 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2517 }
2518 }
2519
2520 if(desc.m_PeepholeEnabled)
2521 {
2522 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2523 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2524 }
2525
2526 if (desc.m_LayerNormEnabled)
2527 {
2528 if(!desc.m_CifgEnabled)
2529 {
2530 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2531 }
2532 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2533 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2534 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2535 }
2536
2537 bool isSupported = false;
2538 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2539 IsLstmSupported,
2540 data.m_Backends,
2541 isSupported,
2542 inputInfo,
2543 outputStateInInfo,
2544 cellStateInInfo,
Pablo Tello972603f2019-11-28 15:21:41 +00002545 fixSbInfo,
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002546 outputStateOutInfo,
2547 cellStateOutInfo,
2548 outputInfo,
2549 desc,
2550 paramsInfo);
2551 if (!isSupported)
2552 {
2553 return false;
2554 }
2555
2556 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002557 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002558
2559 input.Connect(layer->GetInputSlot(0));
2560 outputStateIn.Connect(layer->GetInputSlot(1));
2561 cellStateIn.Connect(layer->GetInputSlot(2));
2562
Pablo Tello972603f2019-11-28 15:21:41 +00002563
2564 return (
2565 (IsDynamicTensor(scratchBufferInfo)?
2566 SetupAndTrackLayerOutputSlotAndOverrideTensorInfo<hal_1_2::HalPolicy>(
2567 operation, 0, *layer, 0, model, data,fixSbInfo):
2568 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(
2569 operation, 0, *layer, 0, model, data)) &&
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002570 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2571 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2572 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2573}
2574
Sadik Armagan701d9a02019-09-04 15:16:18 +01002575bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2576{
2577 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002578 ActivationDescriptor desc;
2579 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002580
2581 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2582}
2583
Mike Kelly46272802019-08-14 17:00:48 +01002584bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2585{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002586 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002587 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2588}
2589
2590bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2591{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002592 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002593 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2594}
2595
2596bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2597{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002598 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002599 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2600}
2601
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002602bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002603{
2604 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2605
2606 if (!input.IsValid())
2607 {
2608 return Fail("%s: Operation has invalid inputs", __func__);
2609 }
2610
2611 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2612
2613 if (!output)
2614 {
2615 return Fail("%s: Could not read output 0", __func__);
2616 }
2617
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002618 const TensorInfo& inputInfo = input.GetTensorInfo();
2619 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002620 if (IsDynamicTensor(outputInfo))
2621 {
2622 return Fail("%s: Dynamic output tensors are not supported", __func__);
2623 }
2624
2625 // ArmNN does not currently support non-fixed weights or bias
2626 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2627 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2628
2629 if (weightsOperand == nullptr)
2630 {
2631 return Fail("%s: Operand is invalid", __func__);
2632 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002633 TransposeConvolution2dDescriptor desc;
2634 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002635
2636 // Determine whether padding is implicit or explicit
2637 bool implicitPadding = operation.inputs.size() == 9;
2638
2639 if (implicitPadding )
2640 {
2641 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2642 }
2643 else
2644 {
2645 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2646 }
2647
2648 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2649 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2650 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2651
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002652 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002653
2654 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2655 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002656 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002657 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2658 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2659
2660 // Bias is a 1D tensor
2661 const ConstTensorPin biasPin =
2662 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2663
2664 if (!weightsPin.IsValid())
2665 {
2666 return Fail("%s: Operation has invalid weights", __func__);
2667 }
2668
2669 if (!biasPin.IsValid())
2670 {
2671 return Fail("%s: Operation has invalid biases", __func__);
2672 }
2673
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002674 ConstTensor weights = weightsPin.GetConstTensor();
2675 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002676 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2677
2678 ActivationFn activation;
2679
2680 if (implicitPadding)
2681 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002682 int32_t strideX{0};
2683 int32_t strideY{0};
2684 int32_t padLeft{0};
2685 int32_t padRight{0};
2686 int32_t padTop{0};
2687 int32_t padBottom{0};
2688
David Monahan613b49c2019-06-27 11:37:47 +01002689 android::nn::PaddingScheme paddingScheme;
2690 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002691 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2692 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002693 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2694 {
2695 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2696 }
2697
2698 const uint32_t kernelX = weights.GetShape()[widthIndex];
2699 const uint32_t kernelY = weights.GetShape()[heightIndex];
Mike Kelly26123db2020-01-15 10:02:33 +00002700 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2701 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002702
Mike Kelly26123db2020-01-15 10:02:33 +00002703 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2704 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002705
2706 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2707 // but Arm NN only supports values >= 0
2708 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2709 {
2710 return Fail("%s: Negative padding values are not supported", __func__);
2711 }
2712
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002713 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2714 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002715 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2716 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2717 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2718 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002719 }
2720 else if (operation.inputs.size() == 11)
2721 {
2722 // explicit padding
2723 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2724 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2725 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2726 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2727 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2728 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2729 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2730 {
2731 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2732 }
2733 }
2734 else
2735 {
2736 return Fail("%s: Unsupported number of operation inputs", __func__);
2737 }
2738
2739 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002740 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002741
2742 bool isSupported = false;
2743 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2744 IsTransposeConvolution2dSupported,
2745 data.m_Backends,
2746 isSupported,
2747 inputInfo,
2748 outputInfo,
2749 desc,
2750 weights.GetInfo(),
2751 biases);
2752 if (!isSupported)
2753 {
2754 return false;
2755 }
2756
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002757 IConnectableLayer* startLayer =
2758 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002759 if (!startLayer)
2760 {
2761 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2762 }
2763
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002764 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002765 if (!endLayer)
2766 {
2767 return Fail("%s: ProcessActivation failed", __func__);
2768 }
2769
2770 input.Connect(startLayer->GetInputSlot(0));
2771
2772 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2773}
2774
Mike Kellyb5fdf382019-06-11 16:35:25 +01002775} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002776} // namespace armnn_driver