blob: ca92318e78c6f517b4b6eef695b86df06132e73c [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);
Sadik Armagancae7e922020-03-23 14:35:05 +0000153 case V1_2::OperationType::NEG:
154 return ConvertElementwiseUnary(operation, model, data, UnaryOperation::Neg);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000155 case V1_2::OperationType::NOT_EQUAL:
156 return ConvertComparison(operation, model, data, ComparisonOperation::NotEqual);
Mike Kelly3c673942019-07-25 09:26:06 +0100157 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100158 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100159 case V1_2::OperationType::PAD_V2:
160 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100161 case V1_2::OperationType::PRELU:
162 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +0100163 case V1_2::OperationType::QUANTIZE:
164 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100165 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
166 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100167 case V1_2::OperationType::RELU:
168 return ConvertReLu(operation, model, data);
169 case V1_2::OperationType::RELU1:
170 return ConvertReLu1(operation, model, data);
171 case V1_2::OperationType::RELU6:
172 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100173 case V1_2::OperationType::RESHAPE:
174 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100175 case V1_2::OperationType::RESIZE_BILINEAR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100176 return ConvertResize(operation, model, data, ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100177 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100178 return ConvertResize(operation, model, data, ResizeMethod::NearestNeighbor);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +0100179 case V1_2::OperationType::RSQRT:
josh minor00a963b2020-01-08 11:55:35 -0600180 return ConvertElementwiseUnary(operation, model, data, UnaryOperation::Rsqrt);
Sadik Armagan701d9a02019-09-04 15:16:18 +0100181 case V1_2::OperationType::SQRT:
182 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100183 case V1_2::OperationType::SQUEEZE:
184 return ConvertSqueeze(operation, model, data);
185 case V1_2::OperationType::STRIDED_SLICE:
186 return ConvertStridedSlice(operation, model, data);
187 case V1_2::OperationType::TRANSPOSE:
188 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +0100189 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +0100190 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +0100191 case V1_2::OperationType::SOFTMAX:
192 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +0100193 case V1_2::OperationType::SPACE_TO_BATCH_ND :
194 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100195 case V1_2::OperationType::SPACE_TO_DEPTH:
196 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100197 case V1_2::OperationType::SUB:
198 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100199 case V1_2::OperationType::TANH:
200 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100201 default:
202 return Fail("%s: Operation type %s not supported in ArmnnDriver",
203 __func__, toString(operation.type).c_str());
204 }
205}
206
Mike Kelly46272802019-08-14 17:00:48 +0100207bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
208{
209 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
210 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
211}
212
Francis Murtagh19fa0cc2019-11-19 12:06:47 +0000213bool HalPolicy::ConvertArgMinMax(const V1_2::Operation& operation,
214 const V1_2::Model& model,
215 ConversionData& data,
216 armnn::ArgMinMaxFunction argMinMaxFunction)
217{
218 ALOGV("hal_1_2::HalPolicy::ConvertArgMinMax()");
219 return ::ConvertArgMinMax<hal_1_2::HalPolicy>(operation, model, data, argMinMaxFunction);
220}
221
Sadik Armagan15d63e22019-07-26 16:59:35 +0100222bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
223{
224 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100225 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Average, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100226}
227
Finn Williams23b87b32019-07-30 11:44:05 +0100228bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
229{
230 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
231 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
232}
233
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000234bool HalPolicy::ConvertComparison(const Operation& operation,
235 const Model& model,
236 ConversionData& data,
237 ComparisonOperation comparisonOperation)
238{
239 ALOGV("hal_1_2::HalPolicy::ConvertComparison()");
240 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
241
242 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
243 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
244
245 if (!(input0.IsValid() && input1.IsValid()))
246 {
247 return Fail("%s: Operation has invalid inputs", __func__);
248 }
249
250 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
251 if (!output)
252 {
253 return Fail("%s: Could not read output 0", __func__);
254 }
255
256 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
257 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
258 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
259
260 if (IsDynamicTensor(outputInfo))
261 {
262 return Fail("%s: Dynamic output tensors are not supported", __func__);
263 }
264
265 ComparisonDescriptor descriptor(comparisonOperation);
266
267 bool isSupported = false;
268 FORWARD_LAYER_SUPPORT_FUNC(__func__,
269 IsComparisonSupported,
270 data.m_Backends,
271 isSupported,
272 inputInfo0,
273 inputInfo1,
274 outputInfo,
275 descriptor);
276
277 if (!isSupported)
278 {
279 return false;
280 }
281
282 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
283 assert(layer != nullptr);
Sadik Armagan793a70c2020-03-19 13:54:04 +0000284 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
285 if (!isReshapeSupported)
286 {
287 return false;
288 }
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000289
290 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
291}
292
Mike Kellyb8805202019-07-31 17:25:43 +0100293bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
294{
295 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
296 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
297}
298
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100299bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
300{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100301 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
302
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100303 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
304 if (!input.IsValid())
305 {
306 return Fail("%s: Operation has invalid inputs", __func__);
307 }
308
309 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
310 if (!output)
311 {
312 return Fail("%s: Could not read output 0", __func__);
313 }
314
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100315 const TensorInfo& inputInfo = input.GetTensorInfo();
316 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100317
318 if (IsDynamicTensor(outputInfo))
319 {
320 return Fail("%s: Dynamic output tensors are not supported", __func__);
321 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100322
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100323 Convolution2dDescriptor desc;
324 desc.m_DataLayout = DataLayout::NHWC;
Mike Kellye1d60bb2019-07-11 11:44:52 +0100325
326 // Determine whether padding is implicit or explicit
327 bool implicitPadding = operation.inputs.size() == 7 ||
328 (operation.inputs.size() >= 8 &&
329 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
330
331 if (implicitPadding)
332 {
333 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
334 }
335 else if (operation.inputs.size() >= 10)
336 {
337 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
338 }
339
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100340 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
Mike Kellye1d60bb2019-07-11 11:44:52 +0100341
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100342 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100343 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
344 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
345 // the DataLayout is NCHW
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100346 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
Mike Kellye1d60bb2019-07-11 11:44:52 +0100347 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
348 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100349 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100350 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100351
352 if (!weightsPin.IsValid())
353 {
354 return Fail("%s: Operation has invalid weights", __func__);
355 }
356
357 if (!biasPin.IsValid())
358 {
359 return Fail("%s: Operation has invalid biases", __func__);
360 }
361
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100362 ConstTensor weights = weightsPin.GetConstTensor();
363 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100364 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
365
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100366 ActivationFn activation;
367
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100368 if (implicitPadding)
369 {
370 android::nn::PaddingScheme paddingScheme;
371 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
372 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
373 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
374 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
375 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
376 {
377 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
378 }
379
Mike Kellye1d60bb2019-07-11 11:44:52 +0100380 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
381 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
382 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
383 const uint32_t kernelX = weights.GetShape()[widthIndex];
384 const uint32_t kernelY = weights.GetShape()[heightIndex];
385 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
386 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100387
Mike Kelly86b36d42019-07-12 16:39:33 +0100388 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
389 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100390
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100391 }
392 else if (operation.inputs.size() >= 10)
393 {
394 // explicit padding
395 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
396 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
397 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
398 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
399 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
400 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
401 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
402 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
403 {
404 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
405 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100406 }
407 else
408 {
409 return Fail("%s: Unsupported number of operation inputs", __func__);
410 }
411
412 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100413 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100414
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100415 bool isSupported = false;
416 FORWARD_LAYER_SUPPORT_FUNC(__func__,
417 IsConvolution2dSupported,
418 data.m_Backends,
419 isSupported,
420 inputInfo,
421 outputInfo,
422 desc,
423 weights.GetInfo(),
424 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100425
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100426 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100427 {
428 return false;
429 }
430
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100431 IConnectableLayer* startLayer =
432 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100433
434 if (!startLayer)
435 {
436 return Fail("%s: AddConvolution2dLayer failed", __func__);
437 }
438
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100439 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100440
441 if (!endLayer)
442 {
443 return Fail("%s: ProcessActivation failed", __func__);
444 }
445
446 input.Connect(startLayer->GetInputSlot(0));
447
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100448 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100449}
450
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100451bool HalPolicy::ConvertDepthToSpace(const Operation& operation, const Model& model, ConversionData& data)
452{
453 ALOGV("hal_1_2::HalPolicy::ConvertDepthToSpace()");
454 return ::ConvertDepthToSpace<hal_1_2::HalPolicy>(operation, model, data);
455}
456
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100457bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
458{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100459 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
460
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100461 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
462
463 if (!input.IsValid())
464 {
465 return Fail("%s: Operation has invalid inputs", __func__);
466 }
467
468 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
469
470 if (!output)
471 {
472 return Fail("%s: Could not read output 0", __func__);
473 }
474
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100475 const TensorInfo& inputInfo = input.GetTensorInfo();
476 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100477
478 if (IsDynamicTensor(outputInfo))
479 {
480 return Fail("%s: Dynamic output tensors are not supported", __func__);
481 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100482
483 // ArmNN does not currently support non-fixed weights or bias
484 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
485 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
486
487 if (weightsOperand == nullptr)
488 {
489 return Fail("%s: Operand is invalid", __func__);
490 }
Teresa Charlin3b959602019-10-31 17:05:47 +0000491 if ( weightsOperand->dimensions[0] != 1)
492 {
493 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
494 __func__, weightsOperand->dimensions[0] );
495 }
496
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100497 DepthwiseConvolution2dDescriptor desc;
498 desc.m_DataLayout = DataLayout::NHWC;
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100499
500 // Determine whether padding is implicit or explicit
501 bool implicitPadding = operation.inputs.size() == 8 ||
502 (operation.inputs.size() >= 9 &&
503 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
504
505 // Look ahead to find the optional DataLayout, if present
506 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
507 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
508
509 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
510 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
511 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
512 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
513
514 // Reinterpret weight data as [ H, W, I, M ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100515 TensorShape weightsShape({ weightsOperand->dimensions[1],
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100516 weightsOperand->dimensions[2],
517 inputInfo.GetShape()[channelsIndex],
518 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
519
520 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100521 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100522
523 const ConstTensorPin weightsPin =
524 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
525 1,
526 model,
527 data,
528 HWIMToMIHW,
529 &weightsShape);
530
531 // Bias is a 1D tensor
532 const ConstTensorPin biasPin =
533 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
534
535 if (!weightsPin.IsValid())
536 {
537 return Fail("%s: Operation has invalid weights", __func__);
538 }
539
540 if (!biasPin.IsValid())
541 {
542 return Fail("%s: Operation has invalid biases", __func__);
543 }
544
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100545 ConstTensor weights = weightsPin.GetConstTensor();
546 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100547 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
548
549 ActivationFn activation;
550
551 if (implicitPadding)
552 {
553 android::nn::PaddingScheme paddingScheme;
554 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
555 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
556 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
557 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
558 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
559 {
560 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
561 }
562
563 const uint32_t kernelX = weights.GetShape()[3];
564 const uint32_t kernelY = weights.GetShape()[2];
565 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
566 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
567
Mike Kelly86b36d42019-07-12 16:39:33 +0100568 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
569 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100570 }
571 else if (operation.inputs.size() >= 11)
572 {
573 // explicit padding
574 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
575 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
576 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
577 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
578 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
579 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
580 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
581 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
582 {
583 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
584 }
585 }
586 else
587 {
588 return Fail("%s: Unsupported number of operation inputs", __func__);
589 }
590
591 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100592 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100593
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100594 bool isSupported = false;
595 FORWARD_LAYER_SUPPORT_FUNC(__func__,
596 IsDepthwiseConvolutionSupported,
597 data.m_Backends,
598 isSupported,
599 inputInfo,
600 outputInfo,
601 desc,
602 weights.GetInfo(),
603 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100604
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100605 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100606 {
607 return false;
608 }
609
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100610 IConnectableLayer* startLayer =
611 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100612
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100613 if (!startLayer)
614 {
615 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
616 }
617
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100618 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100619 if (!endLayer)
620 {
621 return Fail("%s: ProcessActivation failed", __func__);
622 }
623
624 input.Connect(startLayer->GetInputSlot(0));
625
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100626 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100627}
628
Mike Kelly46272802019-08-14 17:00:48 +0100629bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
630{
631 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000632
633 if (IsQSymmDequantizeForWeights(operation, model))
634 {
635 // NOTE: QSymm8 weights are dequantized internally by the driver,
636 // therefore this type of Dequantize is implicitly supported
637 return true;
638 }
639
Mike Kelly46272802019-08-14 17:00:48 +0100640 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
641}
642
643bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
644{
645 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
646 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
647}
648
josh minor00a963b2020-01-08 11:55:35 -0600649bool HalPolicy::ConvertElementwiseUnary(const Operation& operation,
650 const Model& model,
651 ConversionData& data,
652 UnaryOperation unaryOperation)
653{
654 ALOGV("hal_1_2::HalPolicy::ConvertElementwiseUnary()");
655 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
656
657 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
658
659 if (!input.IsValid())
660 {
661 return Fail("%s: Operation has invalid input", __func__);
662 }
663
664 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
665 if (!output)
666 {
667 return Fail("%s: Could not read output 0", __func__);
668 }
669
670 const TensorInfo& inputInfo = input.GetTensorInfo();
671 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
672
673 if (IsDynamicTensor(outputInfo))
674 {
675 return Fail("%s: Dynamic output tensors are not supported", __func__);
676 }
677
678 ElementwiseUnaryDescriptor descriptor(unaryOperation);
679
680 bool isSupported = false;
681 FORWARD_LAYER_SUPPORT_FUNC(__func__,
682 IsElementwiseUnarySupported,
683 data.m_Backends,
684 isSupported,
685 inputInfo,
686 outputInfo,
687 descriptor);
688
689 if (!isSupported)
690 {
691 return false;
692 }
693
694 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
695 assert(layer != nullptr);
696
697 input.Connect(layer->GetInputSlot(0));
698
699 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
700}
701
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100702bool HalPolicy::ConvertExpandDims(const Operation& operation, const Model& model, ConversionData& data)
703{
704 ALOGV("hal_1_2::HalPolicy::ConvertExpandDims()");
705
706 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
707
708 if (!input.IsValid())
709 {
710 return Fail("%s: Operation has invalid input", __func__);
711 }
712
713 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
714 if (!output)
715 {
716 return Fail("%s: Operation has invalid output", __func__);
717 }
718
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100719 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100720 if (IsDynamicTensor(outputInfo))
721 {
722 return Fail("%s: Dynamic output tensors are not supported", __func__);
723 }
724
725 int32_t axis;
726 if (!GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, axis, model, data))
727 {
728 return Fail("%s: failed to get axis input value", __func__);
729 }
730
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100731 TensorShape targetShape;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100732
733 try
734 {
735 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
736 }
737 catch (const std::exception &e)
738 {
739 return Fail("%s: %s", __func__, e.what());
740 }
741
742 if (targetShape != outputInfo.GetShape())
743 {
744 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
745 }
746
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100747 ReshapeDescriptor reshapeDescriptor;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100748 reshapeDescriptor.m_TargetShape = targetShape;
749
750 bool isSupported = false;
751 FORWARD_LAYER_SUPPORT_FUNC(__func__,
752 IsReshapeSupported,
753 data.m_Backends,
754 isSupported,
755 input.GetTensorInfo(),
Kevin Mayaed08ac2019-12-12 16:33:31 +0000756 outputInfo,
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100757 reshapeDescriptor);
758
759 if (!isSupported)
760 {
761 return false;
762 }
763
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100764 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100765 assert(layer != nullptr);
766 input.Connect(layer->GetInputSlot(0));
767
768 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
769}
770
Mike Kelly46272802019-08-14 17:00:48 +0100771bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
772{
773 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
774 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
775}
776
777bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
778{
779 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
780 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
781}
782
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100783bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
784{
785 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
786
787 //
788 // Parse data
789 //
790 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
791 if (!input.IsValid())
792 {
793 return Fail("%s: Operation has invalid inputs", __func__);
794 }
795 const TensorInfo& inputInfo = input.GetTensorInfo();
796
797 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
798 if (!output)
799 {
800 return Fail("%s: Could not read output 0", __func__);
801 }
802 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
803 if (IsDynamicTensor(outputInfo))
804 {
805 return Fail("%s: Dynamic output tensors are not supported", __func__);
806 }
807
808 // Look ahead to determine data layout
809 DataLayout dataLayout = DataLayout::NHWC;
810 if (operation.inputs.size() == 12)
811 {
812 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
813 }
814 else
815 {
816 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
817 }
818
819 // NOTE:
820 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
821 // but Arm NN expects the filter's height and width indices to match the input's height and
822 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
823 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
824 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
825 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
826 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
827 const ConstTensorPin biasesPin =
828 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
829 if (!weightsPin.IsValid() || !biasesPin.IsValid())
830 {
831 return Fail("%s: Operation has invalid inputs", __func__);
832 }
833
834 ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000835 ConstTensor biases = biasesPin.GetConstTensor();
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100836 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
837
838 const TensorShape& inputShape = inputInfo.GetShape();
839 const TensorShape& outputShape = outputInfo.GetShape();
840 const TensorShape& weightsShape = weights.GetShape();
841 const TensorShape& biasesShape = biases.GetShape();
842
843 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
844 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
845 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
846 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
847
848 Convolution2dDescriptor desc;
849 desc.m_DataLayout = dataLayout;
850 desc.m_BiasEnabled = true;
851
852 int numGroups;
853 ActivationFn activation;
854
855 if (operation.inputs.size() == 12)
856 {
857 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
858 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
859 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
860 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
861 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
862 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
863 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
864 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
865 {
866 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
867 }
868
869 }
870 else if (operation.inputs.size() == 9)
871 {
872 android::nn::PaddingScheme paddingScheme;
873 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
874 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
875 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
876 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
877 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
878 {
879 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
880 }
881
882 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
883 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
884
885 const uint32_t kernelX = weightsShape[widthIndex];
886 const uint32_t kernelY = weightsShape[heightIndex];
887
888 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
889 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
890 }
891 else
892 {
893 return Fail("%s: Unsupported number of operation inputs", __func__);
894 }
895
896 const unsigned int outputChannels = outputShape[channelsIndex];
897
898 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
899 const unsigned int channelMultiplier = outputChannels / numGroups;
900
901 //
902 // Validate all relevant inputs
903 //
904 if (numGroups <= 0)
905 {
906 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
907 }
908
909 if (outputChannels % numGroups != 0u)
910 {
911 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
912 }
913
914 //
915 // Set up Splitter layer
916 //
917 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
918 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
919
920 TensorInfo splitterOutputInfo(4,
921 splitterDimSizes,
922 inputInfo.GetDataType(),
923 inputInfo.GetQuantizationScale(),
924 inputInfo.GetQuantizationOffset());
925
926 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
927
928 ViewsDescriptor splitterDesc(numGroups);
929 for (unsigned int group = 0u; group < numGroups; ++group)
930 {
931 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
932 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
933 {
934 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
935 }
936 }
937
938 bool isSupported = false;
939 FORWARD_LAYER_SUPPORT_FUNC(__func__,
940 IsSplitterSupported,
941 data.m_Backends,
942 isSupported,
943 inputInfo,
944 splitterOutputInfos,
945 splitterDesc);
946 if (!isSupported)
947 {
948 return false;
949 }
950
951 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
952 if (!splitterLayer)
953 {
954 return Fail("%s: Failed to add SplitterLayer", __func__);
955 }
956
957 input.Connect(splitterLayer->GetInputSlot(0));
958 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
959 {
960 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
961 }
962
963 //
964 // Set up Convolution2d layers for each group
965 //
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000966
967 // Set up group tensor shapes
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100968 TensorShape groupInputShape(inputShape);
969 groupInputShape[channelsIndex] = channelsPerGroup;
970
971 TensorShape groupOutputShape(outputShape);
972 groupOutputShape[channelsIndex] = 1;
973
974 TensorShape groupWeightsShape(weightsShape);
975 groupWeightsShape[0] /= channelMultiplier * numGroups;
976
977 TensorShape groupBiasesShape({ 1 });
978
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000979 // Set up group tensor infos
980 TensorInfo groupInputInfo(inputInfo);
981 groupInputInfo.SetShape(groupInputShape);
982
983 const TensorInfo& weightsInfo = weights.GetInfo();
984 TensorInfo groupWeightsInfo(weightsInfo);
985 groupWeightsInfo.SetShape(groupWeightsShape);
986
987 const TensorInfo& biasesInfo = biases.GetInfo();
988 TensorInfo groupBiasesInfo(biasesInfo);
989 groupBiasesInfo.SetShape(groupBiasesShape);
990
991 TensorInfo groupOutputInfo(outputInfo);
992 groupOutputInfo.SetShape(groupOutputShape);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100993
994 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
995 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
996
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000997 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100998 for (unsigned int group = 0u; group < numGroups; ++group)
999 {
1000 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1001 {
1002 auto index = group * channelMultiplier + m;
1003
1004 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1005 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1006
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +00001007 if (weightsInfo.HasPerAxisQuantization())
1008 {
1009 // Extract per-axis quantization scales for group weights
1010 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1011 groupWeightsInfo.SetQuantizationScales(
1012 std::vector<float>(weightsQuantScales.begin() + index,
1013 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1014
1015 // Extract per-axis quantization scales for group biases
1016 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1017 groupBiasesInfo.SetQuantizationScales(
1018 std::vector<float>(biasesQuantScales.begin() + index,
1019 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1020 }
1021
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001022 // Extract weights and biases data for current group convolution
1023 ConstTensor groupWeights(groupWeightsInfo,
1024 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1025 weightsDataOffset));
1026 ConstTensor groupBiases(groupBiasesInfo,
1027 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1028 biasesDataOffset));
1029
1030 isSupported = false;
1031 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1032 IsConvolution2dSupported,
1033 data.m_Backends,
1034 isSupported,
1035 groupInputInfo,
1036 groupOutputInfo,
1037 desc,
1038 groupWeightsInfo,
1039 Optional<TensorInfo>(groupBiasesInfo));
1040 if (!isSupported)
1041 {
1042 return false;
1043 }
1044
1045 IConnectableLayer *convLayer =
1046 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
1047 if (!convLayer)
1048 {
1049 return Fail("%s: AddConvolution2dLayer failed", __func__);
1050 }
1051
1052 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1053 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1054
1055 convLayers[index] = convLayer;
1056 }
1057 }
1058
1059 //
1060 // Set up Concat layer
1061 //
1062 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
1063 for (unsigned int group = 0u; group < numGroups; ++group)
1064 {
1065 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1066 {
1067 auto index = group * channelMultiplier + m;
1068 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1069 concatDescriptor.SetConcatAxis(channelsIndex);
1070 }
1071 }
1072
1073 isSupported = false;
1074 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1075 IsConcatSupported,
1076 data.m_Backends,
1077 isSupported,
1078 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1079 outputInfo,
1080 concatDescriptor);
1081 if (!isSupported)
1082 {
1083 return false;
1084 }
1085
1086 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1087 if (!concatLayer)
1088 {
1089 return Fail("%s: AddConcatLayer failed", __func__);
1090 }
1091
1092 for (unsigned int group = 0u; group < numGroups; ++group)
1093 {
1094 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1095 {
1096 auto index = group * channelMultiplier + m;
1097 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1098 }
1099 }
1100 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1101
1102 //
1103 // Set up Activation layer (if it is set)
1104 //
1105 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
1106 if (!endLayer)
1107 {
1108 return Fail("%s: ProcessActivation failed", __func__);
1109 }
1110
1111 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1112}
1113
Aron Virginas-Tara2a73802019-10-09 15:30:40 +01001114bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
1115{
1116 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
1117
1118 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1119 if (!input.IsValid())
1120 {
1121 return Fail("%s: Operation has an invalid input 0", __func__);
1122 }
1123
1124 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1125 if (!output)
1126 {
1127 return Fail("%s: Operation has an invalid output", __func__);
1128 }
1129
1130 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1131 if (IsDynamicTensor(outputInfo))
1132 {
1133 return Fail("%s: Dynamic output tensors are not supported", __func__);
1134 }
1135
1136 // Determine data type of input tensor
1137 OperandType inputType;
1138 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1139 {
1140 return Fail("%s: Operation has invalid inputs", __func__);
1141 }
1142
1143 InstanceNormalizationDescriptor desc;
1144
1145 // Read gamma, beta & epsilon
1146 if (inputType == OperandType::TENSOR_FLOAT16)
1147 {
1148 Half fp16Gamma;
1149 Half fp16Beta;
1150 Half fp16Epsilon;
1151
1152 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1153 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1154 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1155 {
1156 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1157 }
1158
1159 desc.m_Gamma = static_cast<float>(fp16Gamma);
1160 desc.m_Beta = static_cast<float>(fp16Beta);
1161 desc.m_Eps = static_cast<float>(fp16Epsilon);
1162 }
1163 else if (inputType == OperandType::TENSOR_FLOAT32)
1164 {
1165 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1166 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1167 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1168 {
1169 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1170 }
1171 }
1172 else
1173 {
1174 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1175 }
1176
1177 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1178
1179 bool isSupported = false;
1180 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1181 IsInstanceNormalizationSupported,
1182 data.m_Backends,
1183 isSupported,
1184 input.GetTensorInfo(),
1185 outputInfo,
1186 desc);
1187 if (!isSupported)
1188 {
1189 return false;
1190 }
1191
1192 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1193 input.Connect(layer->GetInputSlot(0));
1194
1195 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1196}
1197
Mike Kelly46272802019-08-14 17:00:48 +01001198bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1199{
1200 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1201 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1202}
1203
Sadik Armagan15d63e22019-07-26 16:59:35 +01001204bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1205{
1206 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001207 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001208}
1209
Mike Kelly46272802019-08-14 17:00:48 +01001210bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1211 const Model& model,
1212 ConversionData& data)
1213{
1214 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1215 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1216}
1217
1218bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1219{
1220 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1221 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1222}
1223
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001224bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1225{
1226 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1227
1228 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1229 if (!input.IsValid())
1230 {
1231 return Fail("%s: Failed to read input 0", __func__);
1232 }
1233
1234 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1235 if (!output)
1236 {
1237 return Fail("%s: Failed to read output", __func__);
1238 }
1239
1240 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1241 if (IsDynamicTensor(outputInfo))
1242 {
1243 return Fail("%s: Dynamic output tensors are not supported", __func__);
1244 }
1245
1246 // Determine data type of input tensor
1247 OperandType inputType;
1248 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1249 {
1250 return Fail("%s: Operation has invalid inputs", __func__);
1251 }
1252
1253 LogSoftmaxDescriptor descriptor;
1254
1255 // Read beta
1256 if (inputType == OperandType::TENSOR_FLOAT16)
1257 {
1258 Half fp16Beta;
1259 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1260 {
1261 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1262 }
1263
1264 descriptor.m_Beta = static_cast<float>(fp16Beta);
1265 }
1266 else if (inputType == OperandType::TENSOR_FLOAT32)
1267 {
1268 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1269 {
1270 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1271 }
1272 }
1273 else
1274 {
1275 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1276 }
1277
1278 // Read axis
1279 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1280 {
1281 return Fail("%s: Failed to read input 2", __func__);
1282 }
1283
1284 bool isSupported = false;
1285 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1286 IsLogSoftmaxSupported,
1287 data.m_Backends,
1288 isSupported,
1289 input.GetTensorInfo(),
1290 outputInfo,
1291 descriptor);
1292 if (!isSupported)
1293 {
1294 return false;
1295 }
1296
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001297 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001298 if (!layer)
1299 {
1300 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1301 }
1302
1303 input.Connect(layer->GetInputSlot(0));
1304
1305 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1306}
1307
Sadik Armagan15d63e22019-07-26 16:59:35 +01001308bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1309{
1310 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001311 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001312}
1313
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001314bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1315{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001316 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1317
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001318 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1319 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1320
1321 if (!input0.IsValid() || !input1.IsValid())
1322 {
1323 return Fail("%s: Operation has invalid inputs", __func__);
1324 }
1325
1326 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1327 if (!outputOperand)
1328 {
1329 return Fail("%s: Could not read output", __func__);
1330 }
1331
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001332 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001333 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001334 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001335 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001336 }
1337
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001338 bool isSupported = false;
1339 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1340 IsMaximumSupported,
1341 data.m_Backends,
1342 isSupported,
1343 input0.GetTensorInfo(),
1344 input1.GetTensorInfo(),
1345 outInfo);
1346
1347 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001348 {
1349 return false;
1350 }
1351
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001352 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001353 assert(layer != nullptr);
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001354 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001355 if (!isReshapeSupported)
1356 {
1357 return false;
1358 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001359
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001360 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001361}
1362
Mike Kelly46272802019-08-14 17:00:48 +01001363bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1364{
1365 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1366 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1367}
1368
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001369bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1370{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001371 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1372
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001373 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1374 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1375
1376 if (!input0.IsValid() || !input1.IsValid())
1377 {
1378 return Fail("%s: Operation has invalid inputs", __func__);
1379 }
1380
1381 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1382 if (!output)
1383 {
1384 return Fail("%s: Could not read output 0", __func__);
1385 }
1386
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001387 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001388 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001389 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001390 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001391 }
1392
1393 bool isSupported = false;
1394 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1395 IsMinimumSupported,
1396 data.m_Backends,
1397 isSupported,
1398 input0.GetTensorInfo(),
1399 input1.GetTensorInfo(),
1400 outputInfo);
1401
1402 if (!isSupported)
1403 {
1404 return false;
1405 }
1406
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001407 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001408 assert(layer != nullptr);
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001409 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001410 if (!isReshapeSupported)
1411 {
1412 return false;
1413 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001414
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001415 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001416}
1417
Mike Kelly46272802019-08-14 17:00:48 +01001418bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1419{
1420 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1421 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1422}
1423
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001424bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1425{
1426 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1427 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1428}
1429
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001430bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1431{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001432 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1433
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001434 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1435 if (!input.IsValid())
1436 {
1437 return Fail("%s: Could not read input 0", __func__);
1438 }
1439
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001440 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1441 if (!output)
1442 {
1443 return Fail("%s: Could not read output", __func__);
1444 }
1445
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001446 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001447 unsigned int rank = inputInfo.GetNumDimensions();
1448
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001449 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001450 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1451 {
1452 return Fail("%s: Could not convert paddings", __func__);
1453 }
1454
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001455 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001456 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001457 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001458 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001459 }
1460
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001461 // Determine type of padding value
1462 OperandType operandType0;
1463 OperandType operandType2;
1464
1465 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1466 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1467 {
1468 return Fail("%s: Operation has invalid inputs", __func__);
1469 }
1470
1471 // Read value to use for padding
1472 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1473 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001474 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001475 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1476 {
1477 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1478 }
1479
1480 descriptor.m_PadValue = f16PadValue;
1481 }
1482 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1483 {
1484 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1485 {
1486 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1487 }
1488 }
1489 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1490 {
Mike Kelly3c673942019-07-25 09:26:06 +01001491 int32_t intPadValue = 0;
1492 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001493 {
1494 return Fail("%s: Could not read input 2 (INT32)", __func__);
1495 }
Mike Kelly3c673942019-07-25 09:26:06 +01001496 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001497 }
1498 else
1499 {
1500 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1501 }
1502
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001503 bool isSupported = false;
1504 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1505 IsPadSupported,
1506 data.m_Backends,
1507 isSupported,
1508 inputInfo,
1509 outputInfo,
1510 descriptor);
1511 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001512 {
1513 return false;
1514 }
1515
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001516 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001517 assert(layer != nullptr);
1518 input.Connect(layer->GetInputSlot(0));
1519 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1520
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001521 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001522}
1523
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001524bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1525{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001526 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1527
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001528 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1529 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1530
1531 if (!input.IsValid() || !alpha.IsValid())
1532 {
1533 return Fail("%s: Operation has invalid inputs", __func__);
1534 }
1535
1536 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1537
1538 if (!output)
1539 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001540 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001541 }
1542
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001543 const TensorInfo& inputInfo = input.GetTensorInfo();
1544 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1545 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001546
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001547 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001548 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001549 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001550 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001551
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001552 bool isSupported = false;
1553 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1554 IsPreluSupported,
1555 data.m_Backends,
1556 isSupported,
1557 inputInfo,
1558 alphaInfo,
1559 outputInfo);
1560 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001561 {
1562 return false;
1563 }
1564
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001565 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001566
1567 if (!layer)
1568 {
1569 return Fail("%s: AddPreluLayer failed", __func__);
1570 }
1571
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001572 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001573 if (!isReshapeSupported)
1574 {
1575 return false;
1576 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001577
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001578 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001579}
1580
Sadik Armagan5a476a82019-07-30 09:43:18 +01001581bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1582{
1583 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1584
1585 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1586 if (!input.IsValid())
1587 {
1588 return Fail("%s: Operation has invalid input", __func__);
1589 }
1590
1591 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1592 if (!outputOperand)
1593 {
1594 return Fail("%s: Operation has invalid outputs", __func__);
1595 }
1596
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001597 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001598 if (IsDynamicTensor(outputInfo))
1599 {
1600 return Fail("%s: Dynamic output tensors are not supported", __func__);
1601 }
1602
1603 bool isSupported = false;
1604 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1605 IsQuantizeSupported,
1606 data.m_Backends,
1607 isSupported,
1608 input.GetTensorInfo(),
1609 outputInfo);
1610 if (!isSupported)
1611 {
1612 return false;
1613 }
1614
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001615 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001616 assert(layer != nullptr);
1617 input.Connect(layer->GetInputSlot(0));
1618
1619 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1620}
1621
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001622bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1623{
1624 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1625
1626 //Inputs:
1627 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1628 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1629 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1630 if (!input.IsValid())
1631 {
1632 return Fail("%s: Could not read input 0: input", __func__);
1633 }
1634
1635 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1636 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1637 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1638 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1639 if (!previousCellStateIn.IsValid())
1640 {
1641 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1642 }
1643
1644 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1645 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1646 // is quantized with a fixed quantization range of -1, 127/128.
1647 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1648 if (!previousOutputIn.IsValid())
1649 {
1650 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1651 }
1652
1653 // Get the input tensors:
1654 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1655 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1656 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1657 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001658 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001659
1660 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1661 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1662 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1663 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001664 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001665
1666 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1667 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1668 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1669 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001670 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001671
1672 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1673 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1674 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1675 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001676 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001677
1678 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1679 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1680 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1681 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001682 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001683
1684 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1685 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1686 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1687 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001688 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001689
1690 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1691 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1692 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1693 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001694 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001695
1696 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1697 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1698 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1699 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001700 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001701
1702 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1703 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1704 // of input and weights scales and zeroPoint equal to 0.
1705 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001706 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001707
1708 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1709 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1710 // of input and weights scales and zeroPoint equal to 0.
1711 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001712 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001713
1714 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1715 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1716 // and weights scales and zeroPoint equal to 0.
1717 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001718 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001719
1720 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1721 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1722 // of input and weights scales and zeroPoint equal to 0.
1723 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001724 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001725
1726 if (!inputToInputWeightsPin.IsValid() ||
1727 !inputToForgetWeightsPin.IsValid() ||
1728 !inputToCellWeightsPin.IsValid() ||
1729 !inputToOutputWeightsPin.IsValid() ||
1730 !recurrentToInputWeightsPin.IsValid() ||
1731 !recurrentToForgetWeightsPin.IsValid() ||
1732 !recurrentToCellWeightsPin.IsValid() ||
1733 !recurrentToOutputWeightsPin.IsValid() ||
1734 !inputGateBiasPin.IsValid() ||
1735 !forgetGateBiasPin.IsValid() ||
1736 !cellBiasPin.IsValid() ||
1737 !outputGateBiasPin.IsValid())
1738 {
1739 return Fail("%s: Operation has invalid tensor inputs", __func__);
1740 }
1741
1742 // Outputs:
1743 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1744 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1745 // of -2^4, 2^4 * 32767/32768.
1746 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1747 if (!cellStateOut)
1748 {
1749 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1750 }
1751
1752 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1753 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1754 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1755 if (!output)
1756 {
1757 return Fail("%s: Could not read output 1: output", __func__);
1758 }
1759
1760 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001761 const TensorInfo& inputInfo = input.GetTensorInfo();
1762 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1763 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001764
1765 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001766 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1767 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001768
1769 // Dynamic tensors currently not supported
1770 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1771 {
1772 return Fail("%s: Dynamic output tensors are not supported", __func__);
1773 }
1774
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001775 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001776
1777 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1778 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1779 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1780 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1781 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1782 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1783 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1784 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1785 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1786 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1787 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1788 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1789
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001790 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001791 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1792 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1793 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1794 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1795 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1796 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1797 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1798 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1799 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1800 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1801 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1802 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1803
1804 bool isSupported = false;
1805 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1806 IsQuantizedLstmSupported,
1807 data.m_Backends,
1808 isSupported,
1809 inputInfo,
1810 previousCellStateInInfo,
1811 previousOutputInInfo,
1812 cellStateOutInfo,
1813 outputInfo,
1814 paramsInfo);
1815
1816 if (!isSupported)
1817 {
1818 return false;
1819 }
1820
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001821 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001822 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001823 previousCellStateIn.Connect(layer->GetInputSlot(1));
1824 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001825
1826 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1827 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1828}
1829
Sadik Armagan61113162019-07-25 09:09:40 +01001830bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1831{
1832 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1833 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1834}
1835
1836bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1837{
1838 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1839 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1840}
1841
1842bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1843{
1844 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1845 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1846}
1847
Mike Kelly46272802019-08-14 17:00:48 +01001848bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1849{
1850 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1851 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1852}
1853
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001854bool HalPolicy::ConvertResize(const Operation& operation,
1855 const Model& model,
1856 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001857 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001858{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001859 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001860 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001861
1862 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001863 if (!input.IsValid())
1864 {
1865 return Fail("%s: Could not read input 0", __func__);
1866 }
1867
1868 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1869 if (!output)
1870 {
1871 return Fail("%s: Could not read output 0", __func__);
1872 }
1873
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001874 const TensorInfo& inputInfo = input.GetTensorInfo();
1875 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001876
1877 if (IsDynamicTensor(outputInfo))
1878 {
1879 return Fail("%s: Dynamic output tensors are not supported", __func__);
1880 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001881
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001882 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001883 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001884 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1885
1886 OperandType operandType1;
1887 OperandType operandType2;
1888
1889 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1890 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1891 {
1892 return Fail("%s: Operation has invalid inputs", __func__);
1893 }
1894
1895 if (operandType1 != operandType2)
1896 {
1897 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1898 }
1899
1900 if (operandType1 == OperandType::INT32)
1901 {
1902 // Case 1: resizing by shape
1903 int32_t targetWidth = 0;
1904 int32_t targetHeight = 0;
1905
1906 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1907 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1908 {
1909 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1910 }
1911
1912 if (targetWidth < 0 || targetHeight < 0)
1913 {
1914 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1915 "Target width/height cannot be < 0", __func__);
1916 }
1917
1918 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001919 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001920 }
1921 else if (operandType1 == OperandType::FLOAT32)
1922 {
1923 // Case 2: resizing by scale
1924 float widthScale = 1.0f;
1925 float heightScale = 1.0f;
1926
1927 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1928 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1929 {
1930 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1931 }
1932
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001933 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001934 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1935
1936 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1937 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1938
1939 descriptor.m_TargetWidth = std::floor(width * widthScale);
1940 descriptor.m_TargetHeight = std::floor(height * heightScale);
1941 }
Keith Davisd410b602019-12-19 12:31:30 +00001942 else if (operandType1 == OperandType::FLOAT16)
1943 {
1944 Half widthScale;
1945 Half heightScale;
1946
1947 if (!GetInputScalar<HalPolicy>(operation, 1, HalPolicy::OperandType::FLOAT16, widthScale, model, data) ||
1948 !GetInputScalar<HalPolicy>(operation, 2, HalPolicy::OperandType::FLOAT16, heightScale, model, data))
1949 {
1950 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1951 }
1952
1953 const TensorShape& inputShape = inputInfo.GetShape();
1954 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1955
1956 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
1957 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
1958
1959 descriptor.m_TargetWidth = std::floor(width * widthScale);
1960 descriptor.m_TargetHeight = std::floor(height * heightScale);
1961 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001962 else
1963 {
Keith Davisd410b602019-12-19 12:31:30 +00001964 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001965 }
1966
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001967 bool isSupported = false;
1968 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1969 IsResizeSupported,
1970 data.m_Backends,
1971 isSupported,
1972 inputInfo,
1973 outputInfo,
1974 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001975
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001976 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001977 {
1978 return false;
1979 }
1980
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001981 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001982
1983 assert(layer != nullptr);
1984
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001985 input.Connect(layer->GetInputSlot(0));
1986
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001987 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001988}
1989
Finn Williamsd74c5052019-07-30 17:06:00 +01001990bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1991{
1992 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1993 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1994}
1995
Keith Davisa6bc52f2019-06-26 09:39:49 +01001996bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1997{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001998 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001999
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002000 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002001 if (!input.IsValid() )
2002 {
2003 return Fail("%s: Operation has invalid inputs", __func__);
2004 }
2005
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002006 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01002007 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01002008 if (rank != 4)
2009 {
2010 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2011 }
2012
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002013 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2014 if (!output)
2015 {
2016 return Fail("%s: Could not read output 0", __func__);
2017 }
2018
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002019 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002020 if (IsDynamicTensor(outputInfo))
2021 {
2022 return Fail("%s: Dynamic output tensors are not supported", __func__);
2023 }
2024
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002025 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01002026
2027 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
2028
2029 if (desc.m_BlockSize <= 1)
2030 {
2031 return Fail("%s: Block size must be at least 1 in all dimensions");
2032 }
2033
2034 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
2035
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002036 bool isSupported = false;
2037 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2038 IsSpaceToDepthSupported,
2039 data.m_Backends,
2040 isSupported,
2041 inputInfo,
2042 outputInfo,
2043 desc);
2044 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01002045 {
2046 return false;
2047 }
2048
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002049 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002050 assert(layer != nullptr);
2051 input.Connect(layer->GetInputSlot(0));
2052
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002053 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002054}
2055
Francis Murtagh074c25a2019-07-22 16:40:57 +01002056bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
2057{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002058 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
2059
Francis Murtagh074c25a2019-07-22 16:40:57 +01002060 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2061 if (!input.IsValid())
2062 {
2063 return Fail("%s: Operation has invalid inputs", __func__);
2064 }
2065
2066 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2067 if (!outputOperand)
2068 {
2069 return Fail("%s: Operation has no outputs", __func__);
2070 }
2071
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002072 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01002073 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01002074 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002075 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002076 }
2077
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002078 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01002079 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
2080 {
2081 return Fail("%s: Operation has invalid inputs", __func__);
2082 }
2083
2084 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
2085 2,
2086 HalPolicy::OperandType::INT32,
2087 desc.m_Axis,
2088 model,
2089 data))
2090 {
2091 return Fail("%s: Operation has invalid inputs", __func__);
2092 }
2093
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01002094 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2095 !(desc.m_Axis == 1 ||
2096 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2097 {
2098 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2099 }
2100
Francis Murtagh074c25a2019-07-22 16:40:57 +01002101 bool isSupported = false;
2102 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2103 IsSoftmaxSupported,
2104 data.m_Backends,
2105 isSupported,
2106 input.GetTensorInfo(),
2107 outputInfo,
2108 desc);
2109 if (!isSupported)
2110 {
2111 return false;
2112 }
2113
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002114 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002115 assert(layer != nullptr);
2116 input.Connect(layer->GetInputSlot(0));
2117
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002118 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002119}
2120
Mike Kelly0a879362019-07-29 16:56:31 +01002121bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2122{
2123 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2124 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2125}
2126
Sadik Armagan61113162019-07-25 09:09:40 +01002127bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2128{
2129 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2130 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2131}
2132
Pablo Tello972603f2019-11-28 15:21:41 +00002133template<typename HalPolicy,
2134 typename HalOperation = typename HalPolicy::Operation,
2135 typename HalModel = typename HalPolicy::Model>
2136bool SetupAndTrackLayerOutputSlotAndOverrideTensorInfo(const HalOperation& operation,
2137 uint32_t operationOutputIndex,
2138 armnn::IConnectableLayer& layer,
2139 uint32_t layerOutputIndex,
2140 const HalModel& model,
2141 ConversionData& data,
2142 const armnn::TensorInfo tensor_info)
2143{
2144 using HalOperand = typename HalPolicy::Operand;
2145
2146 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
2147 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
2148 {
2149 return false;
2150 }
2151
2152 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
2153
2154 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
2155 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
2156
2157 outputSlot.SetTensorInfo(tensor_info);
2158
2159 return true;
2160}
2161
2162
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002163bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2164{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002165 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2166
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002167 // Inputs:
2168 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2169 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2170 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2171 if (!input.IsValid())
2172 {
2173 return Fail("%s: Could not read input 0: input", __func__);
2174 }
2175 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2176 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2177 if (!outputStateIn.IsValid())
2178 {
2179 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2180 }
2181 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2182 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2183 if (!cellStateIn.IsValid())
2184 {
2185 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2186 }
2187
2188 // Get the mandatory input tensors:
2189 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2190 // [num_units, input_size].
2191 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002192 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002193 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2194 // [num_units, input_size].
2195 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002196 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002197 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2198 // [num_units, input_size].
2199 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002200 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002201 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2202 // [num_units, output_size].
2203 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002204 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002205 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2206 // [num_units, output_size].
2207 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002208 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002209 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2210 // [num_units, output_size].
2211 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002212 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002213 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2214 const ConstTensorPin forgetGateBiasPin =
2215 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2216 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2217 const ConstTensorPin cellBiasPin =
2218 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2219 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2220 const ConstTensorPin outputGateBiasPin =
2221 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2222
2223 if (!inputToForgetWeightsPin.IsValid() ||
2224 !inputToCellWeightsPin.IsValid() ||
2225 !inputToOutputWeightsPin.IsValid() ||
2226 !recurrentToForgetWeightsPin.IsValid() ||
2227 !recurrentToCellWeightsPin.IsValid() ||
2228 !recurrentToOutputWeightsPin.IsValid() ||
2229 !forgetGateBiasPin.IsValid() ||
2230 !cellBiasPin.IsValid() ||
2231 !outputGateBiasPin.IsValid())
2232 {
2233 return Fail("%s: Operation has invalid tensor inputs", __func__);
2234 }
2235
2236 // Get the optional input tensors:
2237 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2238 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2239 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002240 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002241 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2242 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2243 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2244 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002245 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002246 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2247 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002248 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002249 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2250 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002251 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002252 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2253 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002254 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002255 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2256 const ConstTensorPin inputGateBiasPin =
2257 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2258 12,
2259 model,
2260 data,
2261 g_DontPermute,
2262 nullptr,
2263 true);
2264
2265 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2266 // [output_size, num_units].
2267 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002268 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002269 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2270 const ConstTensorPin projectionBiasPin =
2271 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2272 17,
2273 model,
2274 data,
2275 g_DontPermute,
2276 nullptr,
2277 true);
2278
2279 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2280 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2281 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2282 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2283 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2284 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2285 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2286 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2287 {
2288 return Fail("%s: Operation has invalid tensor inputs", __func__);
2289 }
2290
2291 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2292 // 20: The activation function: A value indicating the activation function:
2293 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2294 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2295 // If set to 0.0 then clipping is disabled.
2296 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2297 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2298 ActivationFn activation;
2299 float cellClip;
2300 float projClip;
2301 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2302 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2303 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2304 {
2305 return Fail("%s: Operation has invalid scalar inputs", __func__);
2306 }
2307
2308 // Get the normalization tensors
2309 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2310 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002311 const ConstTensorPin inputLayerNormWeightsPin
2312 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002313
2314 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2315 // Used to rescale normalized inputs to activation at forget gate.
2316 const ConstTensorPin forgetLayerNormWeightsPin =
2317 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2318 24,
2319 model,
2320 data,
2321 g_DontPermute,
2322 nullptr,
2323 true);
2324
2325 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2326 // Used to rescale normalized inputs to activation at cell gate.
2327 const ConstTensorPin cellLayerNormWeightsPin =
2328 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2329 25,
2330 model,
2331 data,
2332 g_DontPermute,
2333 nullptr,
2334 true);
2335
2336 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2337 // Used to rescale normalized inputs to activation at output gate.
2338 const ConstTensorPin outputLayerNormWeightsPin =
2339 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2340 26,
2341 model,
2342 data,
2343 g_DontPermute,
2344 nullptr,
2345 true);
2346
2347 // Outputs:
2348 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2349 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2350 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2351 if (!scratchBuffer)
2352 {
2353 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2354 }
2355 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2356 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2357 if (!outputStateOut)
2358 {
2359 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2360 }
2361 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2362 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2363 if (!cellStateOut)
2364 {
2365 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2366 }
2367 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2368 // effectively the same as the current “output state (out)” value.
2369 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2370 if (!output)
2371 {
2372 return Fail("%s: Could not read output 3: output", __func__);
2373 }
2374
2375 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002376 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002377 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2378 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2379 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2380 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2381 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2382 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2383 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2384 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2385 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2386 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2387 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2388 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2389 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2390 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2391 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2392 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2393 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2394 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2395 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2396 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2397 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2398
2399 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002400 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002401 desc.m_ActivationFunc = activation;
2402 desc.m_ClippingThresCell = cellClip;
2403 desc.m_ClippingThresProj = projClip;
2404 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2405 params.m_RecurrentToInputWeights == nullptr ||
2406 params.m_InputGateBias == nullptr);
2407 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2408 params.m_CellToOutputWeights != nullptr);
2409 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2410 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2411 params.m_ForgetLayerNormWeights != nullptr ||
2412 params.m_CellLayerNormWeights != nullptr ||
2413 params.m_OutputLayerNormWeights != nullptr);
2414
2415 // validate the optional input groups
2416 if (desc.m_CifgEnabled &&
2417 (params.m_InputToInputWeights != nullptr ||
2418 params.m_RecurrentToInputWeights != nullptr ||
2419 params.m_InputGateBias != nullptr))
2420 {
2421 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2422 " and input gate bias must be provided", __func__);
2423 }
2424
2425 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2426 {
2427 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2428 }
2429
2430 if (desc.m_PeepholeEnabled &&
2431 (params.m_CellToForgetWeights == nullptr ||
2432 params.m_CellToOutputWeights == nullptr ||
2433 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2434 {
2435 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2436 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2437 }
2438
2439 if (desc.m_LayerNormEnabled &&
2440 (params.m_ForgetLayerNormWeights == nullptr ||
2441 params.m_CellLayerNormWeights == nullptr ||
2442 params.m_OutputLayerNormWeights == nullptr ||
2443 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2444 {
2445 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2446 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2447 }
2448
2449 // Check if the layer is supported
2450 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002451 const TensorInfo& inputInfo = input.GetTensorInfo();
2452 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2453 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002454
2455 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002456 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2457 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2458 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2459 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002460
Pablo Tello972603f2019-11-28 15:21:41 +00002461 // Check if the scratch buffer shape was initialized,
2462 // In some cases the shape could be (0,0) which requires the driver
2463 // to infer the shape and set it up accordingly.
2464 // The code below does that.
2465 TensorInfo fixSbInfo = scratchBufferInfo;
2466 if (IsDynamicTensor(scratchBufferInfo))
2467 {
2468 auto & s = fixSbInfo.GetShape();
2469 s[0] = outputStateInInfo.GetShape()[0];
2470 if (desc.m_CifgEnabled)
2471 {
2472 // 2D tensor with dimensions [num_units * 3, batch_size] with CIFG
2473 s[1] = cellStateOutInfo.GetShape()[1]*3;
2474 }
2475 else
2476 {
2477 // scratch_buffer [num_units * 4, batch_size] without CIFG
2478 s[1] = cellStateOutInfo.GetShape()[1]*4;
2479 }
2480 }
2481
2482 if (IsDynamicTensor(outputStateOutInfo) ||
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002483 IsDynamicTensor(cellStateOutInfo) ||
2484 IsDynamicTensor(outputInfo))
2485 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002486 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2487 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2488 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002489 }
2490
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002491 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002492 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002493 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2494 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2495 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2496 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2497 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2498 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2499 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2500 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2501 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2502
2503 // Optional parameters
2504 if(!desc.m_CifgEnabled)
2505 {
2506 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2507 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2508 if (params.m_CellToInputWeights != nullptr)
2509 {
2510 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2511 }
2512 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2513 }
2514
2515 if(desc.m_ProjectionEnabled)
2516 {
2517 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2518 if (params.m_ProjectionBias != nullptr)
2519 {
2520 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2521 }
2522 }
2523
2524 if(desc.m_PeepholeEnabled)
2525 {
2526 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2527 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2528 }
2529
2530 if (desc.m_LayerNormEnabled)
2531 {
2532 if(!desc.m_CifgEnabled)
2533 {
2534 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2535 }
2536 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2537 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2538 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2539 }
2540
2541 bool isSupported = false;
2542 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2543 IsLstmSupported,
2544 data.m_Backends,
2545 isSupported,
2546 inputInfo,
2547 outputStateInInfo,
2548 cellStateInInfo,
Pablo Tello972603f2019-11-28 15:21:41 +00002549 fixSbInfo,
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002550 outputStateOutInfo,
2551 cellStateOutInfo,
2552 outputInfo,
2553 desc,
2554 paramsInfo);
2555 if (!isSupported)
2556 {
2557 return false;
2558 }
2559
2560 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002561 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002562
2563 input.Connect(layer->GetInputSlot(0));
2564 outputStateIn.Connect(layer->GetInputSlot(1));
2565 cellStateIn.Connect(layer->GetInputSlot(2));
2566
Pablo Tello972603f2019-11-28 15:21:41 +00002567
2568 return (
2569 (IsDynamicTensor(scratchBufferInfo)?
2570 SetupAndTrackLayerOutputSlotAndOverrideTensorInfo<hal_1_2::HalPolicy>(
2571 operation, 0, *layer, 0, model, data,fixSbInfo):
2572 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(
2573 operation, 0, *layer, 0, model, data)) &&
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002574 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2575 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2576 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2577}
2578
Sadik Armagan701d9a02019-09-04 15:16:18 +01002579bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2580{
2581 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002582 ActivationDescriptor desc;
2583 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002584
2585 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2586}
2587
Mike Kelly46272802019-08-14 17:00:48 +01002588bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2589{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002590 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002591 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2592}
2593
2594bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2595{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002596 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002597 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2598}
2599
2600bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2601{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002602 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002603 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2604}
2605
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002606bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002607{
2608 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2609
2610 if (!input.IsValid())
2611 {
2612 return Fail("%s: Operation has invalid inputs", __func__);
2613 }
2614
2615 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2616
2617 if (!output)
2618 {
2619 return Fail("%s: Could not read output 0", __func__);
2620 }
2621
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002622 const TensorInfo& inputInfo = input.GetTensorInfo();
2623 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002624 if (IsDynamicTensor(outputInfo))
2625 {
2626 return Fail("%s: Dynamic output tensors are not supported", __func__);
2627 }
2628
2629 // ArmNN does not currently support non-fixed weights or bias
2630 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2631 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2632
2633 if (weightsOperand == nullptr)
2634 {
2635 return Fail("%s: Operand is invalid", __func__);
2636 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002637 TransposeConvolution2dDescriptor desc;
2638 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002639
2640 // Determine whether padding is implicit or explicit
2641 bool implicitPadding = operation.inputs.size() == 9;
2642
2643 if (implicitPadding )
2644 {
2645 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2646 }
2647 else
2648 {
2649 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2650 }
2651
2652 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2653 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2654 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2655
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002656 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002657
2658 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2659 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002660 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002661 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2662 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2663
2664 // Bias is a 1D tensor
2665 const ConstTensorPin biasPin =
2666 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2667
2668 if (!weightsPin.IsValid())
2669 {
2670 return Fail("%s: Operation has invalid weights", __func__);
2671 }
2672
2673 if (!biasPin.IsValid())
2674 {
2675 return Fail("%s: Operation has invalid biases", __func__);
2676 }
2677
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002678 ConstTensor weights = weightsPin.GetConstTensor();
2679 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002680 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2681
2682 ActivationFn activation;
2683
2684 if (implicitPadding)
2685 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002686 int32_t strideX{0};
2687 int32_t strideY{0};
2688 int32_t padLeft{0};
2689 int32_t padRight{0};
2690 int32_t padTop{0};
2691 int32_t padBottom{0};
2692
David Monahan613b49c2019-06-27 11:37:47 +01002693 android::nn::PaddingScheme paddingScheme;
2694 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002695 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2696 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002697 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2698 {
2699 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2700 }
2701
2702 const uint32_t kernelX = weights.GetShape()[widthIndex];
2703 const uint32_t kernelY = weights.GetShape()[heightIndex];
Mike Kelly26123db2020-01-15 10:02:33 +00002704 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2705 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002706
Mike Kelly26123db2020-01-15 10:02:33 +00002707 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
2708 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002709
2710 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2711 // but Arm NN only supports values >= 0
2712 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2713 {
2714 return Fail("%s: Negative padding values are not supported", __func__);
2715 }
2716
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002717 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2718 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002719 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2720 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2721 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2722 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002723 }
2724 else if (operation.inputs.size() == 11)
2725 {
2726 // explicit padding
2727 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2728 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2729 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2730 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2731 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2732 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2733 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2734 {
2735 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2736 }
2737 }
2738 else
2739 {
2740 return Fail("%s: Unsupported number of operation inputs", __func__);
2741 }
2742
2743 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002744 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002745
2746 bool isSupported = false;
2747 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2748 IsTransposeConvolution2dSupported,
2749 data.m_Backends,
2750 isSupported,
2751 inputInfo,
2752 outputInfo,
2753 desc,
2754 weights.GetInfo(),
2755 biases);
2756 if (!isSupported)
2757 {
2758 return false;
2759 }
2760
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002761 IConnectableLayer* startLayer =
2762 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002763 if (!startLayer)
2764 {
2765 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2766 }
2767
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002768 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002769 if (!endLayer)
2770 {
2771 return Fail("%s: ProcessActivation failed", __func__);
2772 }
2773
2774 input.Connect(startLayer->GetInputSlot(0));
2775
2776 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2777}
2778
Mike Kellyb5fdf382019-06-11 16:35:25 +01002779} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002780} // namespace armnn_driver