blob: 1811688fd0c79812f52be0bc3600174c73c392e0 [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
Kevin Mayec1e5b82020-02-26 17:00:39 +000029bool IsQSymmDequantizeForWeights(const HalPolicy::Operation& operation, const HalPolicy::Model& model)
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +000030{
Kevin Mayec1e5b82020-02-26 17:00:39 +000031 const HalPolicy::Operand* operand = GetInputOperand<hal_1_2::HalPolicy>(operation, 0, model);
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +000032 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);
Sadik Armagan793a70c2020-03-19 13:54:04 +0000282 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
283 if (!isReshapeSupported)
284 {
285 return false;
286 }
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000287
288 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
289}
290
Mike Kellyb8805202019-07-31 17:25:43 +0100291bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
292{
293 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
294 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
295}
296
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100297bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
298{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100299 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
300
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100301 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
302 if (!input.IsValid())
303 {
304 return Fail("%s: Operation has invalid inputs", __func__);
305 }
306
307 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
308 if (!output)
309 {
310 return Fail("%s: Could not read output 0", __func__);
311 }
312
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100313 const TensorInfo& inputInfo = input.GetTensorInfo();
314 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100315
316 if (IsDynamicTensor(outputInfo))
317 {
318 return Fail("%s: Dynamic output tensors are not supported", __func__);
319 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100320
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100321 Convolution2dDescriptor desc;
322 desc.m_DataLayout = DataLayout::NHWC;
Mike Kellye1d60bb2019-07-11 11:44:52 +0100323
324 // Determine whether padding is implicit or explicit
325 bool implicitPadding = operation.inputs.size() == 7 ||
326 (operation.inputs.size() >= 8 &&
327 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
328
329 if (implicitPadding)
330 {
331 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
332 }
333 else if (operation.inputs.size() >= 10)
334 {
335 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
336 }
337
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100338 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
Mike Kellye1d60bb2019-07-11 11:44:52 +0100339
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100340 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100341 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
342 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
343 // the DataLayout is NCHW
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100344 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
Mike Kellye1d60bb2019-07-11 11:44:52 +0100345 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
346 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100347 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100348 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100349
350 if (!weightsPin.IsValid())
351 {
352 return Fail("%s: Operation has invalid weights", __func__);
353 }
354
355 if (!biasPin.IsValid())
356 {
357 return Fail("%s: Operation has invalid biases", __func__);
358 }
359
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100360 ConstTensor weights = weightsPin.GetConstTensor();
361 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100362 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
363
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100364 ActivationFn activation;
365
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100366 if (implicitPadding)
367 {
368 android::nn::PaddingScheme paddingScheme;
369 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
370 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
371 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
372 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
373 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
374 {
375 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
376 }
377
Mike Kellye1d60bb2019-07-11 11:44:52 +0100378 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
379 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
380 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
381 const uint32_t kernelX = weights.GetShape()[widthIndex];
382 const uint32_t kernelY = weights.GetShape()[heightIndex];
383 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
384 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100385
Mike Kelly86b36d42019-07-12 16:39:33 +0100386 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
387 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100388
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100389 }
390 else if (operation.inputs.size() >= 10)
391 {
392 // explicit padding
393 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
394 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
395 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
396 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
397 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
398 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
399 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
400 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
401 {
402 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
403 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100404 }
405 else
406 {
407 return Fail("%s: Unsupported number of operation inputs", __func__);
408 }
409
410 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100411 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100412
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100413 bool isSupported = false;
414 FORWARD_LAYER_SUPPORT_FUNC(__func__,
415 IsConvolution2dSupported,
416 data.m_Backends,
417 isSupported,
418 inputInfo,
419 outputInfo,
420 desc,
421 weights.GetInfo(),
422 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100423
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100424 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100425 {
426 return false;
427 }
428
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100429 IConnectableLayer* startLayer =
430 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100431
432 if (!startLayer)
433 {
434 return Fail("%s: AddConvolution2dLayer failed", __func__);
435 }
436
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100437 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100438
439 if (!endLayer)
440 {
441 return Fail("%s: ProcessActivation failed", __func__);
442 }
443
444 input.Connect(startLayer->GetInputSlot(0));
445
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100446 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100447}
448
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100449bool HalPolicy::ConvertDepthToSpace(const Operation& operation, const Model& model, ConversionData& data)
450{
451 ALOGV("hal_1_2::HalPolicy::ConvertDepthToSpace()");
452 return ::ConvertDepthToSpace<hal_1_2::HalPolicy>(operation, model, data);
453}
454
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100455bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
456{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100457 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
458
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100459 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
460
461 if (!input.IsValid())
462 {
463 return Fail("%s: Operation has invalid inputs", __func__);
464 }
465
466 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
467
468 if (!output)
469 {
470 return Fail("%s: Could not read output 0", __func__);
471 }
472
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100473 const TensorInfo& inputInfo = input.GetTensorInfo();
474 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100475
476 if (IsDynamicTensor(outputInfo))
477 {
478 return Fail("%s: Dynamic output tensors are not supported", __func__);
479 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100480
481 // ArmNN does not currently support non-fixed weights or bias
482 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
483 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
484
485 if (weightsOperand == nullptr)
486 {
487 return Fail("%s: Operand is invalid", __func__);
488 }
Teresa Charlin3b959602019-10-31 17:05:47 +0000489 if ( weightsOperand->dimensions[0] != 1)
490 {
491 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
492 __func__, weightsOperand->dimensions[0] );
493 }
494
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100495 DepthwiseConvolution2dDescriptor desc;
496 desc.m_DataLayout = DataLayout::NHWC;
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100497
498 // Determine whether padding is implicit or explicit
499 bool implicitPadding = operation.inputs.size() == 8 ||
500 (operation.inputs.size() >= 9 &&
501 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
502
503 // Look ahead to find the optional DataLayout, if present
504 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
505 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
506
507 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
508 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
509 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
510 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
511
512 // Reinterpret weight data as [ H, W, I, M ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100513 TensorShape weightsShape({ weightsOperand->dimensions[1],
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100514 weightsOperand->dimensions[2],
515 inputInfo.GetShape()[channelsIndex],
516 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
517
518 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100519 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100520
521 const ConstTensorPin weightsPin =
522 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
523 1,
524 model,
525 data,
526 HWIMToMIHW,
527 &weightsShape);
528
529 // Bias is a 1D tensor
530 const ConstTensorPin biasPin =
531 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
532
533 if (!weightsPin.IsValid())
534 {
535 return Fail("%s: Operation has invalid weights", __func__);
536 }
537
538 if (!biasPin.IsValid())
539 {
540 return Fail("%s: Operation has invalid biases", __func__);
541 }
542
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100543 ConstTensor weights = weightsPin.GetConstTensor();
544 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100545 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
546
547 ActivationFn activation;
548
549 if (implicitPadding)
550 {
551 android::nn::PaddingScheme paddingScheme;
552 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
553 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
554 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
555 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
556 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
557 {
558 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
559 }
560
561 const uint32_t kernelX = weights.GetShape()[3];
562 const uint32_t kernelY = weights.GetShape()[2];
563 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
564 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
565
Mike Kelly86b36d42019-07-12 16:39:33 +0100566 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
567 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100568 }
569 else if (operation.inputs.size() >= 11)
570 {
571 // explicit padding
572 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
573 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
574 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
575 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
576 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
577 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
578 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
579 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
580 {
581 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
582 }
583 }
584 else
585 {
586 return Fail("%s: Unsupported number of operation inputs", __func__);
587 }
588
589 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100590 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100591
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100592 bool isSupported = false;
593 FORWARD_LAYER_SUPPORT_FUNC(__func__,
594 IsDepthwiseConvolutionSupported,
595 data.m_Backends,
596 isSupported,
597 inputInfo,
598 outputInfo,
599 desc,
600 weights.GetInfo(),
601 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100602
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100603 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100604 {
605 return false;
606 }
607
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100608 IConnectableLayer* startLayer =
609 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100610
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100611 if (!startLayer)
612 {
613 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
614 }
615
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100616 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100617 if (!endLayer)
618 {
619 return Fail("%s: ProcessActivation failed", __func__);
620 }
621
622 input.Connect(startLayer->GetInputSlot(0));
623
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100624 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100625}
626
Mike Kelly46272802019-08-14 17:00:48 +0100627bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
628{
629 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000630
631 if (IsQSymmDequantizeForWeights(operation, model))
632 {
633 // NOTE: QSymm8 weights are dequantized internally by the driver,
634 // therefore this type of Dequantize is implicitly supported
635 return true;
636 }
637
Mike Kelly46272802019-08-14 17:00:48 +0100638 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
639}
640
641bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
642{
643 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
644 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
645}
646
josh minor00a963b2020-01-08 11:55:35 -0600647bool HalPolicy::ConvertElementwiseUnary(const Operation& operation,
648 const Model& model,
649 ConversionData& data,
650 UnaryOperation unaryOperation)
651{
652 ALOGV("hal_1_2::HalPolicy::ConvertElementwiseUnary()");
653 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
654
655 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
656
657 if (!input.IsValid())
658 {
659 return Fail("%s: Operation has invalid input", __func__);
660 }
661
662 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
663 if (!output)
664 {
665 return Fail("%s: Could not read output 0", __func__);
666 }
667
668 const TensorInfo& inputInfo = input.GetTensorInfo();
669 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
670
671 if (IsDynamicTensor(outputInfo))
672 {
673 return Fail("%s: Dynamic output tensors are not supported", __func__);
674 }
675
676 ElementwiseUnaryDescriptor descriptor(unaryOperation);
677
678 bool isSupported = false;
679 FORWARD_LAYER_SUPPORT_FUNC(__func__,
680 IsElementwiseUnarySupported,
681 data.m_Backends,
682 isSupported,
683 inputInfo,
684 outputInfo,
685 descriptor);
686
687 if (!isSupported)
688 {
689 return false;
690 }
691
692 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
693 assert(layer != nullptr);
694
695 input.Connect(layer->GetInputSlot(0));
696
697 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
698}
699
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100700bool HalPolicy::ConvertExpandDims(const Operation& operation, const Model& model, ConversionData& data)
701{
702 ALOGV("hal_1_2::HalPolicy::ConvertExpandDims()");
703
704 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
705
706 if (!input.IsValid())
707 {
708 return Fail("%s: Operation has invalid input", __func__);
709 }
710
711 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
712 if (!output)
713 {
714 return Fail("%s: Operation has invalid output", __func__);
715 }
716
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100717 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100718 if (IsDynamicTensor(outputInfo))
719 {
720 return Fail("%s: Dynamic output tensors are not supported", __func__);
721 }
722
723 int32_t axis;
724 if (!GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, axis, model, data))
725 {
726 return Fail("%s: failed to get axis input value", __func__);
727 }
728
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100729 TensorShape targetShape;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100730
731 try
732 {
733 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
734 }
735 catch (const std::exception &e)
736 {
737 return Fail("%s: %s", __func__, e.what());
738 }
739
740 if (targetShape != outputInfo.GetShape())
741 {
742 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
743 }
744
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100745 ReshapeDescriptor reshapeDescriptor;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100746 reshapeDescriptor.m_TargetShape = targetShape;
747
748 bool isSupported = false;
749 FORWARD_LAYER_SUPPORT_FUNC(__func__,
750 IsReshapeSupported,
751 data.m_Backends,
752 isSupported,
753 input.GetTensorInfo(),
Kevin Mayaed08ac2019-12-12 16:33:31 +0000754 outputInfo,
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100755 reshapeDescriptor);
756
757 if (!isSupported)
758 {
759 return false;
760 }
761
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100762 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100763 assert(layer != nullptr);
764 input.Connect(layer->GetInputSlot(0));
765
766 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
767}
768
Mike Kelly46272802019-08-14 17:00:48 +0100769bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
770{
771 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
772 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
773}
774
775bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
776{
777 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
778 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
779}
780
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100781bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
782{
783 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
784
785 //
786 // Parse data
787 //
788 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
789 if (!input.IsValid())
790 {
791 return Fail("%s: Operation has invalid inputs", __func__);
792 }
793 const TensorInfo& inputInfo = input.GetTensorInfo();
794
795 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
796 if (!output)
797 {
798 return Fail("%s: Could not read output 0", __func__);
799 }
800 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
801 if (IsDynamicTensor(outputInfo))
802 {
803 return Fail("%s: Dynamic output tensors are not supported", __func__);
804 }
805
806 // Look ahead to determine data layout
807 DataLayout dataLayout = DataLayout::NHWC;
808 if (operation.inputs.size() == 12)
809 {
810 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
811 }
812 else
813 {
814 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
815 }
816
817 // NOTE:
818 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
819 // but Arm NN expects the filter's height and width indices to match the input's height and
820 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
821 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
822 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
823 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
824 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
825 const ConstTensorPin biasesPin =
826 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
827 if (!weightsPin.IsValid() || !biasesPin.IsValid())
828 {
829 return Fail("%s: Operation has invalid inputs", __func__);
830 }
831
832 ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000833 ConstTensor biases = biasesPin.GetConstTensor();
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100834 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
835
836 const TensorShape& inputShape = inputInfo.GetShape();
837 const TensorShape& outputShape = outputInfo.GetShape();
838 const TensorShape& weightsShape = weights.GetShape();
839 const TensorShape& biasesShape = biases.GetShape();
840
841 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
842 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
843 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
844 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
845
846 Convolution2dDescriptor desc;
847 desc.m_DataLayout = dataLayout;
848 desc.m_BiasEnabled = true;
849
850 int numGroups;
851 ActivationFn activation;
852
853 if (operation.inputs.size() == 12)
854 {
855 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
856 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
857 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
858 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
859 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
860 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
861 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
862 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
863 {
864 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
865 }
866
867 }
868 else if (operation.inputs.size() == 9)
869 {
870 android::nn::PaddingScheme paddingScheme;
871 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
872 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
873 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
874 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
875 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
876 {
877 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
878 }
879
880 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
881 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
882
883 const uint32_t kernelX = weightsShape[widthIndex];
884 const uint32_t kernelY = weightsShape[heightIndex];
885
886 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
887 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
888 }
889 else
890 {
891 return Fail("%s: Unsupported number of operation inputs", __func__);
892 }
893
894 const unsigned int outputChannels = outputShape[channelsIndex];
895
896 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
897 const unsigned int channelMultiplier = outputChannels / numGroups;
898
899 //
900 // Validate all relevant inputs
901 //
902 if (numGroups <= 0)
903 {
904 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
905 }
906
907 if (outputChannels % numGroups != 0u)
908 {
909 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
910 }
911
912 //
913 // Set up Splitter layer
914 //
915 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
916 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
917
918 TensorInfo splitterOutputInfo(4,
919 splitterDimSizes,
920 inputInfo.GetDataType(),
921 inputInfo.GetQuantizationScale(),
922 inputInfo.GetQuantizationOffset());
923
924 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
925
926 ViewsDescriptor splitterDesc(numGroups);
927 for (unsigned int group = 0u; group < numGroups; ++group)
928 {
929 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
930 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
931 {
932 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
933 }
934 }
935
936 bool isSupported = false;
937 FORWARD_LAYER_SUPPORT_FUNC(__func__,
938 IsSplitterSupported,
939 data.m_Backends,
940 isSupported,
941 inputInfo,
942 splitterOutputInfos,
943 splitterDesc);
944 if (!isSupported)
945 {
946 return false;
947 }
948
949 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
950 if (!splitterLayer)
951 {
952 return Fail("%s: Failed to add SplitterLayer", __func__);
953 }
954
955 input.Connect(splitterLayer->GetInputSlot(0));
956 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
957 {
958 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
959 }
960
961 //
962 // Set up Convolution2d layers for each group
963 //
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000964
965 // Set up group tensor shapes
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100966 TensorShape groupInputShape(inputShape);
967 groupInputShape[channelsIndex] = channelsPerGroup;
968
969 TensorShape groupOutputShape(outputShape);
970 groupOutputShape[channelsIndex] = 1;
971
972 TensorShape groupWeightsShape(weightsShape);
973 groupWeightsShape[0] /= channelMultiplier * numGroups;
974
975 TensorShape groupBiasesShape({ 1 });
976
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000977 // Set up group tensor infos
978 TensorInfo groupInputInfo(inputInfo);
979 groupInputInfo.SetShape(groupInputShape);
980
981 const TensorInfo& weightsInfo = weights.GetInfo();
982 TensorInfo groupWeightsInfo(weightsInfo);
983 groupWeightsInfo.SetShape(groupWeightsShape);
984
985 const TensorInfo& biasesInfo = biases.GetInfo();
986 TensorInfo groupBiasesInfo(biasesInfo);
987 groupBiasesInfo.SetShape(groupBiasesShape);
988
989 TensorInfo groupOutputInfo(outputInfo);
990 groupOutputInfo.SetShape(groupOutputShape);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100991
992 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
993 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
994
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000995 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100996 for (unsigned int group = 0u; group < numGroups; ++group)
997 {
998 for (unsigned int m = 0u; m < channelMultiplier; ++m)
999 {
1000 auto index = group * channelMultiplier + m;
1001
1002 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1003 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1004
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +00001005 if (weightsInfo.HasPerAxisQuantization())
1006 {
1007 // Extract per-axis quantization scales for group weights
1008 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1009 groupWeightsInfo.SetQuantizationScales(
1010 std::vector<float>(weightsQuantScales.begin() + index,
1011 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1012
1013 // Extract per-axis quantization scales for group biases
1014 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1015 groupBiasesInfo.SetQuantizationScales(
1016 std::vector<float>(biasesQuantScales.begin() + index,
1017 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1018 }
1019
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001020 // Extract weights and biases data for current group convolution
1021 ConstTensor groupWeights(groupWeightsInfo,
1022 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1023 weightsDataOffset));
1024 ConstTensor groupBiases(groupBiasesInfo,
1025 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1026 biasesDataOffset));
1027
1028 isSupported = false;
1029 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1030 IsConvolution2dSupported,
1031 data.m_Backends,
1032 isSupported,
1033 groupInputInfo,
1034 groupOutputInfo,
1035 desc,
1036 groupWeightsInfo,
1037 Optional<TensorInfo>(groupBiasesInfo));
1038 if (!isSupported)
1039 {
1040 return false;
1041 }
1042
1043 IConnectableLayer *convLayer =
1044 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1045 if (!convLayer)
1046 {
1047 return Fail("%s: AddConvolution2dLayer failed", __func__);
1048 }
1049
1050 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1051 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1052
1053 convLayers[index] = convLayer;
1054 }
1055 }
1056
1057 //
1058 // Set up Concat layer
1059 //
1060 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
1061 for (unsigned int group = 0u; group < numGroups; ++group)
1062 {
1063 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1064 {
1065 auto index = group * channelMultiplier + m;
1066 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1067 concatDescriptor.SetConcatAxis(channelsIndex);
1068 }
1069 }
1070
1071 isSupported = false;
1072 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1073 IsConcatSupported,
1074 data.m_Backends,
1075 isSupported,
1076 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1077 outputInfo,
1078 concatDescriptor);
1079 if (!isSupported)
1080 {
1081 return false;
1082 }
1083
1084 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1085 if (!concatLayer)
1086 {
1087 return Fail("%s: AddConcatLayer failed", __func__);
1088 }
1089
1090 for (unsigned int group = 0u; group < numGroups; ++group)
1091 {
1092 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1093 {
1094 auto index = group * channelMultiplier + m;
1095 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1096 }
1097 }
1098 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1099
1100 //
1101 // Set up Activation layer (if it is set)
1102 //
1103 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
1104 if (!endLayer)
1105 {
1106 return Fail("%s: ProcessActivation failed", __func__);
1107 }
1108
1109 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1110}
1111
Aron Virginas-Tara2a73802019-10-09 15:30:40 +01001112bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
1113{
1114 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
1115
1116 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1117 if (!input.IsValid())
1118 {
1119 return Fail("%s: Operation has an invalid input 0", __func__);
1120 }
1121
1122 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1123 if (!output)
1124 {
1125 return Fail("%s: Operation has an invalid output", __func__);
1126 }
1127
1128 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1129 if (IsDynamicTensor(outputInfo))
1130 {
1131 return Fail("%s: Dynamic output tensors are not supported", __func__);
1132 }
1133
1134 // Determine data type of input tensor
1135 OperandType inputType;
1136 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1137 {
1138 return Fail("%s: Operation has invalid inputs", __func__);
1139 }
1140
1141 InstanceNormalizationDescriptor desc;
1142
1143 // Read gamma, beta & epsilon
1144 if (inputType == OperandType::TENSOR_FLOAT16)
1145 {
1146 Half fp16Gamma;
1147 Half fp16Beta;
1148 Half fp16Epsilon;
1149
1150 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1151 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1152 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1153 {
1154 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1155 }
1156
1157 desc.m_Gamma = static_cast<float>(fp16Gamma);
1158 desc.m_Beta = static_cast<float>(fp16Beta);
1159 desc.m_Eps = static_cast<float>(fp16Epsilon);
1160 }
1161 else if (inputType == OperandType::TENSOR_FLOAT32)
1162 {
1163 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1164 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1165 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1166 {
1167 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1168 }
1169 }
1170 else
1171 {
1172 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1173 }
1174
1175 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1176
1177 bool isSupported = false;
1178 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1179 IsInstanceNormalizationSupported,
1180 data.m_Backends,
1181 isSupported,
1182 input.GetTensorInfo(),
1183 outputInfo,
1184 desc);
1185 if (!isSupported)
1186 {
1187 return false;
1188 }
1189
1190 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1191 input.Connect(layer->GetInputSlot(0));
1192
1193 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1194}
1195
Mike Kelly46272802019-08-14 17:00:48 +01001196bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1197{
1198 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1199 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1200}
1201
Sadik Armagan15d63e22019-07-26 16:59:35 +01001202bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1203{
1204 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001205 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001206}
1207
Mike Kelly46272802019-08-14 17:00:48 +01001208bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1209 const Model& model,
1210 ConversionData& data)
1211{
1212 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1213 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1214}
1215
1216bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1217{
1218 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1219 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1220}
1221
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001222bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1223{
1224 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1225
1226 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1227 if (!input.IsValid())
1228 {
1229 return Fail("%s: Failed to read input 0", __func__);
1230 }
1231
1232 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1233 if (!output)
1234 {
1235 return Fail("%s: Failed to read output", __func__);
1236 }
1237
1238 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1239 if (IsDynamicTensor(outputInfo))
1240 {
1241 return Fail("%s: Dynamic output tensors are not supported", __func__);
1242 }
1243
1244 // Determine data type of input tensor
1245 OperandType inputType;
1246 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1247 {
1248 return Fail("%s: Operation has invalid inputs", __func__);
1249 }
1250
1251 LogSoftmaxDescriptor descriptor;
1252
1253 // Read beta
1254 if (inputType == OperandType::TENSOR_FLOAT16)
1255 {
1256 Half fp16Beta;
1257 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1258 {
1259 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1260 }
1261
1262 descriptor.m_Beta = static_cast<float>(fp16Beta);
1263 }
1264 else if (inputType == OperandType::TENSOR_FLOAT32)
1265 {
1266 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1267 {
1268 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1269 }
1270 }
1271 else
1272 {
1273 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1274 }
1275
1276 // Read axis
1277 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1278 {
1279 return Fail("%s: Failed to read input 2", __func__);
1280 }
1281
1282 bool isSupported = false;
1283 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1284 IsLogSoftmaxSupported,
1285 data.m_Backends,
1286 isSupported,
1287 input.GetTensorInfo(),
1288 outputInfo,
1289 descriptor);
1290 if (!isSupported)
1291 {
1292 return false;
1293 }
1294
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001295 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001296 if (!layer)
1297 {
1298 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1299 }
1300
1301 input.Connect(layer->GetInputSlot(0));
1302
1303 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1304}
1305
Sadik Armagan15d63e22019-07-26 16:59:35 +01001306bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1307{
1308 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001309 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001310}
1311
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001312bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1313{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001314 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1315
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001316 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1317 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1318
1319 if (!input0.IsValid() || !input1.IsValid())
1320 {
1321 return Fail("%s: Operation has invalid inputs", __func__);
1322 }
1323
1324 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1325 if (!outputOperand)
1326 {
1327 return Fail("%s: Could not read output", __func__);
1328 }
1329
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001330 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001331 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001332 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001333 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001334 }
1335
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001336 bool isSupported = false;
1337 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1338 IsMaximumSupported,
1339 data.m_Backends,
1340 isSupported,
1341 input0.GetTensorInfo(),
1342 input1.GetTensorInfo(),
1343 outInfo);
1344
1345 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001346 {
1347 return false;
1348 }
1349
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001350 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001351 assert(layer != nullptr);
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001352 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001353 if (!isReshapeSupported)
1354 {
1355 return false;
1356 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001357
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001358 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001359}
1360
Mike Kelly46272802019-08-14 17:00:48 +01001361bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1362{
1363 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1364 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1365}
1366
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001367bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1368{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001369 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1370
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001371 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1372 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1373
1374 if (!input0.IsValid() || !input1.IsValid())
1375 {
1376 return Fail("%s: Operation has invalid inputs", __func__);
1377 }
1378
1379 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1380 if (!output)
1381 {
1382 return Fail("%s: Could not read output 0", __func__);
1383 }
1384
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001385 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001386 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001387 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001388 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001389 }
1390
1391 bool isSupported = false;
1392 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1393 IsMinimumSupported,
1394 data.m_Backends,
1395 isSupported,
1396 input0.GetTensorInfo(),
1397 input1.GetTensorInfo(),
1398 outputInfo);
1399
1400 if (!isSupported)
1401 {
1402 return false;
1403 }
1404
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001405 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001406 assert(layer != nullptr);
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001407 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001408 if (!isReshapeSupported)
1409 {
1410 return false;
1411 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001412
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001413 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001414}
1415
Mike Kelly46272802019-08-14 17:00:48 +01001416bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1417{
1418 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1419 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1420}
1421
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001422bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1423{
1424 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1425 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1426}
1427
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001428bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1429{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001430 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1431
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001432 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1433 if (!input.IsValid())
1434 {
1435 return Fail("%s: Could not read input 0", __func__);
1436 }
1437
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001438 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1439 if (!output)
1440 {
1441 return Fail("%s: Could not read output", __func__);
1442 }
1443
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001444 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001445 unsigned int rank = inputInfo.GetNumDimensions();
1446
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001447 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001448 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1449 {
1450 return Fail("%s: Could not convert paddings", __func__);
1451 }
1452
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001453 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001454 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001455 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001456 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001457 }
1458
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001459 // Determine type of padding value
1460 OperandType operandType0;
1461 OperandType operandType2;
1462
1463 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1464 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1465 {
1466 return Fail("%s: Operation has invalid inputs", __func__);
1467 }
1468
1469 // Read value to use for padding
1470 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1471 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001472 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001473 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1474 {
1475 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1476 }
1477
1478 descriptor.m_PadValue = f16PadValue;
1479 }
1480 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1481 {
1482 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1483 {
1484 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1485 }
1486 }
1487 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1488 {
Mike Kelly3c673942019-07-25 09:26:06 +01001489 int32_t intPadValue = 0;
1490 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001491 {
1492 return Fail("%s: Could not read input 2 (INT32)", __func__);
1493 }
Mike Kelly3c673942019-07-25 09:26:06 +01001494 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001495 }
1496 else
1497 {
1498 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1499 }
1500
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001501 bool isSupported = false;
1502 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1503 IsPadSupported,
1504 data.m_Backends,
1505 isSupported,
1506 inputInfo,
1507 outputInfo,
1508 descriptor);
1509 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001510 {
1511 return false;
1512 }
1513
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001514 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001515 assert(layer != nullptr);
1516 input.Connect(layer->GetInputSlot(0));
1517 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1518
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001519 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001520}
1521
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001522bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1523{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001524 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1525
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001526 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1527 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1528
1529 if (!input.IsValid() || !alpha.IsValid())
1530 {
1531 return Fail("%s: Operation has invalid inputs", __func__);
1532 }
1533
1534 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1535
1536 if (!output)
1537 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001538 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001539 }
1540
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001541 const TensorInfo& inputInfo = input.GetTensorInfo();
1542 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1543 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001544
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001545 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001546 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001547 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001548 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001549
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001550 bool isSupported = false;
1551 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1552 IsPreluSupported,
1553 data.m_Backends,
1554 isSupported,
1555 inputInfo,
1556 alphaInfo,
1557 outputInfo);
1558 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001559 {
1560 return false;
1561 }
1562
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001563 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001564
1565 if (!layer)
1566 {
1567 return Fail("%s: AddPreluLayer failed", __func__);
1568 }
1569
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001570 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001571 if (!isReshapeSupported)
1572 {
1573 return false;
1574 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001575
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001576 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001577}
1578
Sadik Armagan5a476a82019-07-30 09:43:18 +01001579bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1580{
1581 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1582
1583 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1584 if (!input.IsValid())
1585 {
1586 return Fail("%s: Operation has invalid input", __func__);
1587 }
1588
1589 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1590 if (!outputOperand)
1591 {
1592 return Fail("%s: Operation has invalid outputs", __func__);
1593 }
1594
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001595 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001596 if (IsDynamicTensor(outputInfo))
1597 {
1598 return Fail("%s: Dynamic output tensors are not supported", __func__);
1599 }
1600
1601 bool isSupported = false;
1602 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1603 IsQuantizeSupported,
1604 data.m_Backends,
1605 isSupported,
1606 input.GetTensorInfo(),
1607 outputInfo);
1608 if (!isSupported)
1609 {
1610 return false;
1611 }
1612
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001613 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001614 assert(layer != nullptr);
1615 input.Connect(layer->GetInputSlot(0));
1616
1617 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1618}
1619
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001620bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1621{
1622 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1623
1624 //Inputs:
1625 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1626 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1627 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1628 if (!input.IsValid())
1629 {
1630 return Fail("%s: Could not read input 0: input", __func__);
1631 }
1632
1633 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1634 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1635 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1636 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1637 if (!previousCellStateIn.IsValid())
1638 {
1639 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1640 }
1641
1642 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1643 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1644 // is quantized with a fixed quantization range of -1, 127/128.
1645 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1646 if (!previousOutputIn.IsValid())
1647 {
1648 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1649 }
1650
1651 // Get the input tensors:
1652 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1653 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1654 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1655 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001656 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001657
1658 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1659 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1660 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1661 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001662 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001663
1664 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1665 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1666 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1667 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001668 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001669
1670 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1671 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1672 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1673 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001674 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001675
1676 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1677 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1678 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1679 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001680 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001681
1682 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1683 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1684 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1685 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001686 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001687
1688 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1689 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1690 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1691 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001692 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001693
1694 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1695 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1696 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1697 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001698 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001699
1700 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1701 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1702 // of input and weights scales and zeroPoint equal to 0.
1703 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001704 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001705
1706 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1707 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1708 // of input and weights scales and zeroPoint equal to 0.
1709 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001710 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001711
1712 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1713 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1714 // and weights scales and zeroPoint equal to 0.
1715 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001716 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001717
1718 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1719 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1720 // of input and weights scales and zeroPoint equal to 0.
1721 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001722 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001723
1724 if (!inputToInputWeightsPin.IsValid() ||
1725 !inputToForgetWeightsPin.IsValid() ||
1726 !inputToCellWeightsPin.IsValid() ||
1727 !inputToOutputWeightsPin.IsValid() ||
1728 !recurrentToInputWeightsPin.IsValid() ||
1729 !recurrentToForgetWeightsPin.IsValid() ||
1730 !recurrentToCellWeightsPin.IsValid() ||
1731 !recurrentToOutputWeightsPin.IsValid() ||
1732 !inputGateBiasPin.IsValid() ||
1733 !forgetGateBiasPin.IsValid() ||
1734 !cellBiasPin.IsValid() ||
1735 !outputGateBiasPin.IsValid())
1736 {
1737 return Fail("%s: Operation has invalid tensor inputs", __func__);
1738 }
1739
1740 // Outputs:
1741 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1742 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1743 // of -2^4, 2^4 * 32767/32768.
1744 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1745 if (!cellStateOut)
1746 {
1747 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1748 }
1749
1750 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1751 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1752 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1753 if (!output)
1754 {
1755 return Fail("%s: Could not read output 1: output", __func__);
1756 }
1757
1758 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001759 const TensorInfo& inputInfo = input.GetTensorInfo();
1760 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1761 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001762
1763 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001764 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1765 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001766
1767 // Dynamic tensors currently not supported
1768 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1769 {
1770 return Fail("%s: Dynamic output tensors are not supported", __func__);
1771 }
1772
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001773 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001774
1775 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1776 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1777 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1778 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1779 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1780 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1781 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1782 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1783 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1784 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1785 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1786 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1787
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001788 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001789 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1790 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1791 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1792 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1793 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1794 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1795 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1796 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1797 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1798 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1799 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1800 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1801
1802 bool isSupported = false;
1803 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1804 IsQuantizedLstmSupported,
1805 data.m_Backends,
1806 isSupported,
1807 inputInfo,
1808 previousCellStateInInfo,
1809 previousOutputInInfo,
1810 cellStateOutInfo,
1811 outputInfo,
1812 paramsInfo);
1813
1814 if (!isSupported)
1815 {
1816 return false;
1817 }
1818
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001819 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001820 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001821 previousCellStateIn.Connect(layer->GetInputSlot(1));
1822 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001823
1824 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1825 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1826}
1827
Sadik Armagan61113162019-07-25 09:09:40 +01001828bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1829{
1830 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1831 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1832}
1833
1834bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1835{
1836 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1837 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1838}
1839
1840bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1841{
1842 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1843 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1844}
1845
Mike Kelly46272802019-08-14 17:00:48 +01001846bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1847{
1848 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1849 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1850}
1851
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001852bool HalPolicy::ConvertResize(const Operation& operation,
1853 const Model& model,
1854 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001855 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001856{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001857 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001858 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001859
1860 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001861 if (!input.IsValid())
1862 {
1863 return Fail("%s: Could not read input 0", __func__);
1864 }
1865
1866 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1867 if (!output)
1868 {
1869 return Fail("%s: Could not read output 0", __func__);
1870 }
1871
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001872 const TensorInfo& inputInfo = input.GetTensorInfo();
1873 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001874
1875 if (IsDynamicTensor(outputInfo))
1876 {
1877 return Fail("%s: Dynamic output tensors are not supported", __func__);
1878 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001879
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001880 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001881 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001882 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1883
1884 OperandType operandType1;
1885 OperandType operandType2;
1886
1887 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1888 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1889 {
1890 return Fail("%s: Operation has invalid inputs", __func__);
1891 }
1892
1893 if (operandType1 != operandType2)
1894 {
1895 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1896 }
1897
1898 if (operandType1 == OperandType::INT32)
1899 {
1900 // Case 1: resizing by shape
1901 int32_t targetWidth = 0;
1902 int32_t targetHeight = 0;
1903
1904 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1905 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1906 {
1907 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1908 }
1909
1910 if (targetWidth < 0 || targetHeight < 0)
1911 {
1912 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1913 "Target width/height cannot be < 0", __func__);
1914 }
1915
1916 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001917 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001918 }
1919 else if (operandType1 == OperandType::FLOAT32)
1920 {
1921 // Case 2: resizing by scale
1922 float widthScale = 1.0f;
1923 float heightScale = 1.0f;
1924
1925 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1926 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1927 {
1928 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1929 }
1930
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001931 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001932 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1933
1934 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1935 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1936
1937 descriptor.m_TargetWidth = std::floor(width * widthScale);
1938 descriptor.m_TargetHeight = std::floor(height * heightScale);
1939 }
Keith Davisd410b602019-12-19 12:31:30 +00001940 else if (operandType1 == OperandType::FLOAT16)
1941 {
1942 Half widthScale;
1943 Half heightScale;
1944
1945 if (!GetInputScalar<HalPolicy>(operation, 1, HalPolicy::OperandType::FLOAT16, widthScale, model, data) ||
1946 !GetInputScalar<HalPolicy>(operation, 2, HalPolicy::OperandType::FLOAT16, heightScale, model, data))
1947 {
1948 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1949 }
1950
1951 const TensorShape& inputShape = inputInfo.GetShape();
1952 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1953
1954 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
1955 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
1956
1957 descriptor.m_TargetWidth = std::floor(width * widthScale);
1958 descriptor.m_TargetHeight = std::floor(height * heightScale);
1959 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001960 else
1961 {
Keith Davisd410b602019-12-19 12:31:30 +00001962 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001963 }
1964
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001965 bool isSupported = false;
1966 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1967 IsResizeSupported,
1968 data.m_Backends,
1969 isSupported,
1970 inputInfo,
1971 outputInfo,
1972 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001973
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001974 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001975 {
1976 return false;
1977 }
1978
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001979 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001980
1981 assert(layer != nullptr);
1982
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001983 input.Connect(layer->GetInputSlot(0));
1984
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001985 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001986}
1987
Finn Williamsd74c5052019-07-30 17:06:00 +01001988bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1989{
1990 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1991 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1992}
1993
Keith Davisa6bc52f2019-06-26 09:39:49 +01001994bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1995{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001996 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001997
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001998 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001999 if (!input.IsValid() )
2000 {
2001 return Fail("%s: Operation has invalid inputs", __func__);
2002 }
2003
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002004 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01002005 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01002006 if (rank != 4)
2007 {
2008 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2009 }
2010
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002011 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2012 if (!output)
2013 {
2014 return Fail("%s: Could not read output 0", __func__);
2015 }
2016
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002017 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002018 if (IsDynamicTensor(outputInfo))
2019 {
2020 return Fail("%s: Dynamic output tensors are not supported", __func__);
2021 }
2022
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002023 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01002024
2025 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
2026
2027 if (desc.m_BlockSize <= 1)
2028 {
2029 return Fail("%s: Block size must be at least 1 in all dimensions");
2030 }
2031
2032 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
2033
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002034 bool isSupported = false;
2035 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2036 IsSpaceToDepthSupported,
2037 data.m_Backends,
2038 isSupported,
2039 inputInfo,
2040 outputInfo,
2041 desc);
2042 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01002043 {
2044 return false;
2045 }
2046
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002047 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002048 assert(layer != nullptr);
2049 input.Connect(layer->GetInputSlot(0));
2050
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002051 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002052}
2053
Francis Murtagh074c25a2019-07-22 16:40:57 +01002054bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
2055{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002056 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
2057
Francis Murtagh074c25a2019-07-22 16:40:57 +01002058 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2059 if (!input.IsValid())
2060 {
2061 return Fail("%s: Operation has invalid inputs", __func__);
2062 }
2063
2064 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2065 if (!outputOperand)
2066 {
2067 return Fail("%s: Operation has no outputs", __func__);
2068 }
2069
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002070 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01002071 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01002072 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002073 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002074 }
2075
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002076 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01002077 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
2078 {
2079 return Fail("%s: Operation has invalid inputs", __func__);
2080 }
2081
2082 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
2083 2,
2084 HalPolicy::OperandType::INT32,
2085 desc.m_Axis,
2086 model,
2087 data))
2088 {
2089 return Fail("%s: Operation has invalid inputs", __func__);
2090 }
2091
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01002092 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2093 !(desc.m_Axis == 1 ||
2094 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2095 {
2096 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2097 }
2098
Francis Murtagh074c25a2019-07-22 16:40:57 +01002099 bool isSupported = false;
2100 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2101 IsSoftmaxSupported,
2102 data.m_Backends,
2103 isSupported,
2104 input.GetTensorInfo(),
2105 outputInfo,
2106 desc);
2107 if (!isSupported)
2108 {
2109 return false;
2110 }
2111
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002112 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002113 assert(layer != nullptr);
2114 input.Connect(layer->GetInputSlot(0));
2115
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002116 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002117}
2118
Mike Kelly0a879362019-07-29 16:56:31 +01002119bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2120{
2121 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2122 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2123}
2124
Sadik Armagan61113162019-07-25 09:09:40 +01002125bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2126{
2127 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2128 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2129}
2130
Pablo Tello972603f2019-11-28 15:21:41 +00002131template<typename HalPolicy,
2132 typename HalOperation = typename HalPolicy::Operation,
2133 typename HalModel = typename HalPolicy::Model>
2134bool SetupAndTrackLayerOutputSlotAndOverrideTensorInfo(const HalOperation& operation,
2135 uint32_t operationOutputIndex,
2136 armnn::IConnectableLayer& layer,
2137 uint32_t layerOutputIndex,
2138 const HalModel& model,
2139 ConversionData& data,
2140 const armnn::TensorInfo tensor_info)
2141{
2142 using HalOperand = typename HalPolicy::Operand;
2143
2144 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
2145 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
2146 {
2147 return false;
2148 }
2149
2150 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
2151
2152 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
2153 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
2154
2155 outputSlot.SetTensorInfo(tensor_info);
2156
2157 return true;
2158}
2159
2160
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002161bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2162{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002163 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2164
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002165 // Inputs:
2166 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2167 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2168 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2169 if (!input.IsValid())
2170 {
2171 return Fail("%s: Could not read input 0: input", __func__);
2172 }
2173 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2174 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2175 if (!outputStateIn.IsValid())
2176 {
2177 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2178 }
2179 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2180 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2181 if (!cellStateIn.IsValid())
2182 {
2183 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2184 }
2185
2186 // Get the mandatory input tensors:
2187 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2188 // [num_units, input_size].
2189 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002190 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002191 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2192 // [num_units, input_size].
2193 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002194 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002195 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2196 // [num_units, input_size].
2197 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002198 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002199 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2200 // [num_units, output_size].
2201 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002202 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002203 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2204 // [num_units, output_size].
2205 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002206 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002207 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2208 // [num_units, output_size].
2209 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002210 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002211 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2212 const ConstTensorPin forgetGateBiasPin =
2213 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2214 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2215 const ConstTensorPin cellBiasPin =
2216 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2217 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2218 const ConstTensorPin outputGateBiasPin =
2219 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2220
2221 if (!inputToForgetWeightsPin.IsValid() ||
2222 !inputToCellWeightsPin.IsValid() ||
2223 !inputToOutputWeightsPin.IsValid() ||
2224 !recurrentToForgetWeightsPin.IsValid() ||
2225 !recurrentToCellWeightsPin.IsValid() ||
2226 !recurrentToOutputWeightsPin.IsValid() ||
2227 !forgetGateBiasPin.IsValid() ||
2228 !cellBiasPin.IsValid() ||
2229 !outputGateBiasPin.IsValid())
2230 {
2231 return Fail("%s: Operation has invalid tensor inputs", __func__);
2232 }
2233
2234 // Get the optional input tensors:
2235 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2236 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2237 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002238 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002239 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2240 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2241 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2242 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002243 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002244 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2245 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002246 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002247 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2248 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002249 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002250 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2251 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002252 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002253 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2254 const ConstTensorPin inputGateBiasPin =
2255 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2256 12,
2257 model,
2258 data,
2259 g_DontPermute,
2260 nullptr,
2261 true);
2262
2263 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2264 // [output_size, num_units].
2265 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002266 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002267 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2268 const ConstTensorPin projectionBiasPin =
2269 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2270 17,
2271 model,
2272 data,
2273 g_DontPermute,
2274 nullptr,
2275 true);
2276
2277 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2278 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2279 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2280 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2281 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2282 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2283 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2284 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2285 {
2286 return Fail("%s: Operation has invalid tensor inputs", __func__);
2287 }
2288
2289 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2290 // 20: The activation function: A value indicating the activation function:
2291 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2292 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2293 // If set to 0.0 then clipping is disabled.
2294 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2295 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2296 ActivationFn activation;
2297 float cellClip;
2298 float projClip;
2299 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2300 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2301 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2302 {
2303 return Fail("%s: Operation has invalid scalar inputs", __func__);
2304 }
2305
2306 // Get the normalization tensors
2307 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2308 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002309 const ConstTensorPin inputLayerNormWeightsPin
2310 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002311
2312 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2313 // Used to rescale normalized inputs to activation at forget gate.
2314 const ConstTensorPin forgetLayerNormWeightsPin =
2315 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2316 24,
2317 model,
2318 data,
2319 g_DontPermute,
2320 nullptr,
2321 true);
2322
2323 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2324 // Used to rescale normalized inputs to activation at cell gate.
2325 const ConstTensorPin cellLayerNormWeightsPin =
2326 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2327 25,
2328 model,
2329 data,
2330 g_DontPermute,
2331 nullptr,
2332 true);
2333
2334 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2335 // Used to rescale normalized inputs to activation at output gate.
2336 const ConstTensorPin outputLayerNormWeightsPin =
2337 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2338 26,
2339 model,
2340 data,
2341 g_DontPermute,
2342 nullptr,
2343 true);
2344
2345 // Outputs:
2346 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2347 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2348 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2349 if (!scratchBuffer)
2350 {
2351 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2352 }
2353 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2354 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2355 if (!outputStateOut)
2356 {
2357 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2358 }
2359 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2360 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2361 if (!cellStateOut)
2362 {
2363 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2364 }
2365 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2366 // effectively the same as the current “output state (out)” value.
2367 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2368 if (!output)
2369 {
2370 return Fail("%s: Could not read output 3: output", __func__);
2371 }
2372
2373 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002374 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002375 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2376 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2377 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2378 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2379 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2380 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2381 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2382 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2383 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2384 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2385 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2386 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2387 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2388 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2389 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2390 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2391 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2392 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2393 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2394 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2395 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2396
2397 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002398 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002399 desc.m_ActivationFunc = activation;
2400 desc.m_ClippingThresCell = cellClip;
2401 desc.m_ClippingThresProj = projClip;
2402 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2403 params.m_RecurrentToInputWeights == nullptr ||
2404 params.m_InputGateBias == nullptr);
2405 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2406 params.m_CellToOutputWeights != nullptr);
2407 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2408 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2409 params.m_ForgetLayerNormWeights != nullptr ||
2410 params.m_CellLayerNormWeights != nullptr ||
2411 params.m_OutputLayerNormWeights != nullptr);
2412
2413 // validate the optional input groups
2414 if (desc.m_CifgEnabled &&
2415 (params.m_InputToInputWeights != nullptr ||
2416 params.m_RecurrentToInputWeights != nullptr ||
2417 params.m_InputGateBias != nullptr))
2418 {
2419 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2420 " and input gate bias must be provided", __func__);
2421 }
2422
2423 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2424 {
2425 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2426 }
2427
2428 if (desc.m_PeepholeEnabled &&
2429 (params.m_CellToForgetWeights == nullptr ||
2430 params.m_CellToOutputWeights == nullptr ||
2431 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2432 {
2433 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2434 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2435 }
2436
2437 if (desc.m_LayerNormEnabled &&
2438 (params.m_ForgetLayerNormWeights == nullptr ||
2439 params.m_CellLayerNormWeights == nullptr ||
2440 params.m_OutputLayerNormWeights == nullptr ||
2441 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2442 {
2443 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2444 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2445 }
2446
2447 // Check if the layer is supported
2448 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002449 const TensorInfo& inputInfo = input.GetTensorInfo();
2450 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2451 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002452
2453 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002454 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2455 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2456 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2457 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002458
Pablo Tello972603f2019-11-28 15:21:41 +00002459 // Check if the scratch buffer shape was initialized,
2460 // In some cases the shape could be (0,0) which requires the driver
2461 // to infer the shape and set it up accordingly.
2462 // The code below does that.
2463 TensorInfo fixSbInfo = scratchBufferInfo;
2464 if (IsDynamicTensor(scratchBufferInfo))
2465 {
2466 auto & s = fixSbInfo.GetShape();
2467 s[0] = outputStateInInfo.GetShape()[0];
2468 if (desc.m_CifgEnabled)
2469 {
2470 // 2D tensor with dimensions [num_units * 3, batch_size] with CIFG
2471 s[1] = cellStateOutInfo.GetShape()[1]*3;
2472 }
2473 else
2474 {
2475 // scratch_buffer [num_units * 4, batch_size] without CIFG
2476 s[1] = cellStateOutInfo.GetShape()[1]*4;
2477 }
2478 }
2479
2480 if (IsDynamicTensor(outputStateOutInfo) ||
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002481 IsDynamicTensor(cellStateOutInfo) ||
2482 IsDynamicTensor(outputInfo))
2483 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002484 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2485 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2486 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002487 }
2488
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002489 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002490 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002491 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2492 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2493 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2494 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2495 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2496 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2497 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2498 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2499 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2500
2501 // Optional parameters
2502 if(!desc.m_CifgEnabled)
2503 {
2504 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2505 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2506 if (params.m_CellToInputWeights != nullptr)
2507 {
2508 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2509 }
2510 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2511 }
2512
2513 if(desc.m_ProjectionEnabled)
2514 {
2515 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2516 if (params.m_ProjectionBias != nullptr)
2517 {
2518 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2519 }
2520 }
2521
2522 if(desc.m_PeepholeEnabled)
2523 {
2524 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2525 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2526 }
2527
2528 if (desc.m_LayerNormEnabled)
2529 {
2530 if(!desc.m_CifgEnabled)
2531 {
2532 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2533 }
2534 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2535 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2536 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2537 }
2538
2539 bool isSupported = false;
2540 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2541 IsLstmSupported,
2542 data.m_Backends,
2543 isSupported,
2544 inputInfo,
2545 outputStateInInfo,
2546 cellStateInInfo,
Pablo Tello972603f2019-11-28 15:21:41 +00002547 fixSbInfo,
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002548 outputStateOutInfo,
2549 cellStateOutInfo,
2550 outputInfo,
2551 desc,
2552 paramsInfo);
2553 if (!isSupported)
2554 {
2555 return false;
2556 }
2557
2558 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002559 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002560
2561 input.Connect(layer->GetInputSlot(0));
2562 outputStateIn.Connect(layer->GetInputSlot(1));
2563 cellStateIn.Connect(layer->GetInputSlot(2));
2564
Pablo Tello972603f2019-11-28 15:21:41 +00002565
2566 return (
2567 (IsDynamicTensor(scratchBufferInfo)?
2568 SetupAndTrackLayerOutputSlotAndOverrideTensorInfo<hal_1_2::HalPolicy>(
2569 operation, 0, *layer, 0, model, data,fixSbInfo):
2570 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(
2571 operation, 0, *layer, 0, model, data)) &&
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002572 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2573 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2574 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2575}
2576
Sadik Armagan701d9a02019-09-04 15:16:18 +01002577bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2578{
2579 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002580 ActivationDescriptor desc;
2581 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002582
2583 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2584}
2585
Mike Kelly46272802019-08-14 17:00:48 +01002586bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2587{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002588 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002589 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2590}
2591
2592bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2593{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002594 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002595 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2596}
2597
2598bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2599{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002600 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002601 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2602}
2603
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002604bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002605{
2606 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2607
2608 if (!input.IsValid())
2609 {
2610 return Fail("%s: Operation has invalid inputs", __func__);
2611 }
2612
2613 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2614
2615 if (!output)
2616 {
2617 return Fail("%s: Could not read output 0", __func__);
2618 }
2619
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002620 const TensorInfo& inputInfo = input.GetTensorInfo();
2621 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002622 if (IsDynamicTensor(outputInfo))
2623 {
2624 return Fail("%s: Dynamic output tensors are not supported", __func__);
2625 }
2626
2627 // ArmNN does not currently support non-fixed weights or bias
2628 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2629 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2630
2631 if (weightsOperand == nullptr)
2632 {
2633 return Fail("%s: Operand is invalid", __func__);
2634 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002635 TransposeConvolution2dDescriptor desc;
2636 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002637
2638 // Determine whether padding is implicit or explicit
2639 bool implicitPadding = operation.inputs.size() == 9;
2640
2641 if (implicitPadding )
2642 {
2643 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2644 }
2645 else
2646 {
2647 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2648 }
2649
2650 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2651 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2652 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2653
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002654 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002655
2656 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2657 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002658 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002659 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2660 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2661
2662 // Bias is a 1D tensor
2663 const ConstTensorPin biasPin =
2664 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2665
2666 if (!weightsPin.IsValid())
2667 {
2668 return Fail("%s: Operation has invalid weights", __func__);
2669 }
2670
2671 if (!biasPin.IsValid())
2672 {
2673 return Fail("%s: Operation has invalid biases", __func__);
2674 }
2675
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002676 ConstTensor weights = weightsPin.GetConstTensor();
2677 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002678 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2679
2680 ActivationFn activation;
2681
2682 if (implicitPadding)
2683 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002684 int32_t strideX{0};
2685 int32_t strideY{0};
2686 int32_t padLeft{0};
2687 int32_t padRight{0};
2688 int32_t padTop{0};
2689 int32_t padBottom{0};
2690
David Monahan613b49c2019-06-27 11:37:47 +01002691 android::nn::PaddingScheme paddingScheme;
2692 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002693 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2694 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002695 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2696 {
2697 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2698 }
2699
2700 const uint32_t kernelX = weights.GetShape()[widthIndex];
2701 const uint32_t kernelY = weights.GetShape()[heightIndex];
Mike Kelly26123db2020-01-15 10:02:33 +00002702 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2703 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002704
Mike Kelly26123db2020-01-15 10:02:33 +00002705 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2706 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002707
2708 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2709 // but Arm NN only supports values >= 0
2710 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2711 {
2712 return Fail("%s: Negative padding values are not supported", __func__);
2713 }
2714
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002715 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2716 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002717 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2718 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2719 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2720 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002721 }
2722 else if (operation.inputs.size() == 11)
2723 {
2724 // explicit padding
2725 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2726 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2727 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2728 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2729 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2730 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2731 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2732 {
2733 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2734 }
2735 }
2736 else
2737 {
2738 return Fail("%s: Unsupported number of operation inputs", __func__);
2739 }
2740
2741 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002742 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002743
2744 bool isSupported = false;
2745 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2746 IsTransposeConvolution2dSupported,
2747 data.m_Backends,
2748 isSupported,
2749 inputInfo,
2750 outputInfo,
2751 desc,
2752 weights.GetInfo(),
2753 biases);
2754 if (!isSupported)
2755 {
2756 return false;
2757 }
2758
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002759 IConnectableLayer* startLayer =
2760 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002761 if (!startLayer)
2762 {
2763 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2764 }
2765
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002766 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002767 if (!endLayer)
2768 {
2769 return Fail("%s: ProcessActivation failed", __func__);
2770 }
2771
2772 input.Connect(startLayer->GetInputSlot(0));
2773
2774 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2775}
2776
Mike Kellyb5fdf382019-06-11 16:35:25 +01002777} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002778} // namespace armnn_driver