blob: ddd85d9bf66b7620acb369bf022707df64e878fe [file] [log] [blame]
Mike Kellyb5fdf382019-06-11 16:35:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "HalPolicy.hpp"
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01007#include "Utils.hpp"
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01008
Teresa Charlin8f6429d2019-10-01 13:10:15 +01009#include <armnn/TypesUtils.hpp>
10
Matteo Martincigh00d6ed12019-11-28 17:13:24 +000011#include <armnnUtils/DataLayoutIndexed.hpp>
12#include <armnnUtils/TensorUtils.hpp>
13
14#include <Half.hpp>
15
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010016#include <cmath>
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +000017#include <string>
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010018
Mike Kellyb5fdf382019-06-11 16:35:25 +010019namespace armnn_driver
20{
21namespace hal_1_2
22{
23
Teresa Charlin8f6429d2019-10-01 13:10:15 +010024using namespace armnn;
25
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +000026namespace
27{
28
29bool IsQSymmDequantizeForWeights(const Operation& operation, const Model& model)
30{
31 const Operand* operand = GetInputOperand<hal_1_2::HalPolicy>(operation, 0, model);
32 if (!operand)
33 {
34 return false;
35 }
36
37 if(!IsQSymm8(*operand))
38 {
39 // Only QSymm8 weights are dequantized on the fly by the driver
40 return false;
41 }
42
43 if (!IsOperandConstant<hal_1_2::HalPolicy>(*operand))
44 {
45 // Non-const input is not accepted for weights
46 return false;
47 }
48
49 // Iterate through all the operations and find the operation feeding from the Dequantize output
50 const size_t outputIndex = operation.outputs[0];
51 for (uint32_t operationIdx = 0; operationIdx < model.operations.size(); ++operationIdx)
52 {
53 const auto& operationIt = model.operations[operationIdx];
54 switch (operationIt.type)
55 {
56 case HalPolicy::OperationType::FULLY_CONNECTED:
57 if (outputIndex == operationIt.inputs[1]) // Weights are bound to slot 1
58 {
59 // If the output is going into the FC weights return true
60 return true;
61 }
62 break;
63 case HalPolicy::OperationType::LSTM:
64 for (size_t k = 0; k < operationIt.inputs.size(); ++k)
65 {
66 if (outputIndex == operationIt.inputs[k])
67 {
68 // If the output is going into the LSTM weights return true
69 return true;
70 }
71 }
72 break;
73 default:
74 break;
75 }
76 }
77
78 return false;
79}
80
81} // anonymous namespace
82
Mike Kellyb5fdf382019-06-11 16:35:25 +010083bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
84{
Mike Kellyb5fdf382019-06-11 16:35:25 +010085 switch (operation.type)
86 {
Kevin May407718f2019-09-09 14:46:41 +010087 case V1_2::OperationType::ABS:
88 return ConvertAbs(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010089 case V1_2::OperationType::ADD:
90 return ConvertAdd(operation, model, data);
Francis Murtagh19fa0cc2019-11-19 12:06:47 +000091 case V1_2::OperationType::ARGMAX:
92 return ConvertArgMinMax(operation, model, data, ArgMinMaxFunction::Max);
93 case V1_2::OperationType::ARGMIN:
94 return ConvertArgMinMax(operation, model, data, ArgMinMaxFunction::Min);
Sadik Armagan15d63e22019-07-26 16:59:35 +010095 case V1_2::OperationType::AVERAGE_POOL_2D:
96 return ConvertAveragePool2d(operation, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +010097 case V1_2::OperationType::BATCH_TO_SPACE_ND:
98 return ConvertBatchToSpaceNd(operation, model, data);
Mike Kellyb8805202019-07-31 17:25:43 +010099 case V1_2::OperationType::CONCATENATION:
100 return ConvertConcatenation(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100101 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100102 return ConvertConv2d(operation, model, data);
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100103 case V1_2::OperationType::DEPTH_TO_SPACE:
104 return ConvertDepthToSpace(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100105 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100106 return ConvertDepthwiseConv2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100107 case V1_2::OperationType::DEQUANTIZE:
108 return ConvertDequantize(operation, model, data);
109 case V1_2::OperationType::DIV:
110 return ConvertDiv(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000111 case V1_2::OperationType::EQUAL:
112 return ConvertComparison(operation, model, data, ComparisonOperation::Equal);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100113 case V1_2::OperationType::EXPAND_DIMS:
114 return ConvertExpandDims(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100115 case V1_2::OperationType::FLOOR:
116 return ConvertFloor(operation, model, data);
117 case V1_2::OperationType::FULLY_CONNECTED:
118 return ConvertFullyConnected(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000119 case V1_2::OperationType::GREATER:
120 return ConvertComparison(operation, model, data, ComparisonOperation::Greater);
121 case V1_2::OperationType::GREATER_EQUAL:
122 return ConvertComparison(operation, model, data, ComparisonOperation::GreaterOrEqual);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100123 case V1_2::OperationType::GROUPED_CONV_2D:
124 return ConvertGroupedConv2d(operation, model, data);
Aron Virginas-Tara2a73802019-10-09 15:30:40 +0100125 case V1_2::OperationType::INSTANCE_NORMALIZATION:
126 return ConvertInstanceNormalization(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100127 case V1_2::OperationType::L2_NORMALIZATION:
128 return ConvertL2Normalization(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100129 case V1_2::OperationType::L2_POOL_2D:
130 return ConvertL2Pool2d(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000131 case V1_2::OperationType::LESS:
132 return ConvertComparison(operation, model, data, ComparisonOperation::Less);
133 case V1_2::OperationType::LESS_EQUAL:
134 return ConvertComparison(operation, model, data, ComparisonOperation::LessOrEqual);
Mike Kelly46272802019-08-14 17:00:48 +0100135 case V1_2::OperationType::LOCAL_RESPONSE_NORMALIZATION:
136 return ConvertLocalResponseNormalization(operation, model, data);
137 case V1_2::OperationType::LOGISTIC:
138 return ConvertLogistic(operation, model, data);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +0100139 case V1_2::OperationType::LOG_SOFTMAX:
140 return ConvertLogSoftmax(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100141 case V1_2::OperationType::LSTM:
142 return ConvertLstm(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100143 case V1_2::OperationType::MAX_POOL_2D:
144 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100145 case V1_2::OperationType::MAXIMUM:
146 return ConvertMaximum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100147 case V1_2::OperationType::MEAN:
148 return ConvertMean(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100149 case V1_2::OperationType::MINIMUM:
150 return ConvertMinimum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100151 case V1_2::OperationType::MUL:
152 return ConvertMul(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000153 case V1_2::OperationType::NOT_EQUAL:
154 return ConvertComparison(operation, model, data, ComparisonOperation::NotEqual);
Mike Kelly3c673942019-07-25 09:26:06 +0100155 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100156 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100157 case V1_2::OperationType::PAD_V2:
158 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100159 case V1_2::OperationType::PRELU:
160 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +0100161 case V1_2::OperationType::QUANTIZE:
162 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100163 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
164 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100165 case V1_2::OperationType::RELU:
166 return ConvertReLu(operation, model, data);
167 case V1_2::OperationType::RELU1:
168 return ConvertReLu1(operation, model, data);
169 case V1_2::OperationType::RELU6:
170 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100171 case V1_2::OperationType::RESHAPE:
172 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100173 case V1_2::OperationType::RESIZE_BILINEAR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100174 return ConvertResize(operation, model, data, ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100175 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100176 return ConvertResize(operation, model, data, ResizeMethod::NearestNeighbor);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +0100177 case V1_2::OperationType::RSQRT:
178 return ConvertRsqrt(operation, model, data);
Sadik Armagan701d9a02019-09-04 15:16:18 +0100179 case V1_2::OperationType::SQRT:
180 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100181 case V1_2::OperationType::SQUEEZE:
182 return ConvertSqueeze(operation, model, data);
183 case V1_2::OperationType::STRIDED_SLICE:
184 return ConvertStridedSlice(operation, model, data);
185 case V1_2::OperationType::TRANSPOSE:
186 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +0100187 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +0100188 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +0100189 case V1_2::OperationType::SOFTMAX:
190 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +0100191 case V1_2::OperationType::SPACE_TO_BATCH_ND :
192 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100193 case V1_2::OperationType::SPACE_TO_DEPTH:
194 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100195 case V1_2::OperationType::SUB:
196 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100197 case V1_2::OperationType::TANH:
198 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100199 default:
200 return Fail("%s: Operation type %s not supported in ArmnnDriver",
201 __func__, toString(operation.type).c_str());
202 }
203}
204
Kevin May407718f2019-09-09 14:46:41 +0100205bool HalPolicy::ConvertAbs(const Operation& operation, const Model& model, ConversionData& data)
206{
207 ALOGV("hal_1_2::HalPolicy::ConvertAbs()");
208 return ::ConvertAbs<hal_1_2::HalPolicy>(operation, model, data);
209}
210
Mike Kelly46272802019-08-14 17:00:48 +0100211bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
212{
213 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
214 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
215}
216
Francis Murtagh19fa0cc2019-11-19 12:06:47 +0000217bool HalPolicy::ConvertArgMinMax(const V1_2::Operation& operation,
218 const V1_2::Model& model,
219 ConversionData& data,
220 armnn::ArgMinMaxFunction argMinMaxFunction)
221{
222 ALOGV("hal_1_2::HalPolicy::ConvertArgMinMax()");
223 return ::ConvertArgMinMax<hal_1_2::HalPolicy>(operation, model, data, argMinMaxFunction);
224}
225
Sadik Armagan15d63e22019-07-26 16:59:35 +0100226bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
227{
228 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100229 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Average, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100230}
231
Finn Williams23b87b32019-07-30 11:44:05 +0100232bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
233{
234 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
235 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
236}
237
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000238bool HalPolicy::ConvertComparison(const Operation& operation,
239 const Model& model,
240 ConversionData& data,
241 ComparisonOperation comparisonOperation)
242{
243 ALOGV("hal_1_2::HalPolicy::ConvertComparison()");
244 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
245
246 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
247 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
248
249 if (!(input0.IsValid() && input1.IsValid()))
250 {
251 return Fail("%s: Operation has invalid inputs", __func__);
252 }
253
254 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
255 if (!output)
256 {
257 return Fail("%s: Could not read output 0", __func__);
258 }
259
260 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
261 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
262 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
263
264 if (IsDynamicTensor(outputInfo))
265 {
266 return Fail("%s: Dynamic output tensors are not supported", __func__);
267 }
268
269 ComparisonDescriptor descriptor(comparisonOperation);
270
271 bool isSupported = false;
272 FORWARD_LAYER_SUPPORT_FUNC(__func__,
273 IsComparisonSupported,
274 data.m_Backends,
275 isSupported,
276 inputInfo0,
277 inputInfo1,
278 outputInfo,
279 descriptor);
280
281 if (!isSupported)
282 {
283 return false;
284 }
285
286 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
287 assert(layer != nullptr);
288
289 input0.Connect(layer->GetInputSlot(0));
290 input1.Connect(layer->GetInputSlot(1));
291
292 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
293}
294
Mike Kellyb8805202019-07-31 17:25:43 +0100295bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
296{
297 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
298 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
299}
300
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100301bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
302{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100303 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
304
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100305 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
306 if (!input.IsValid())
307 {
308 return Fail("%s: Operation has invalid inputs", __func__);
309 }
310
311 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
312 if (!output)
313 {
314 return Fail("%s: Could not read output 0", __func__);
315 }
316
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100317 const TensorInfo& inputInfo = input.GetTensorInfo();
318 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100319
320 if (IsDynamicTensor(outputInfo))
321 {
322 return Fail("%s: Dynamic output tensors are not supported", __func__);
323 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100324
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100325 Convolution2dDescriptor desc;
326 desc.m_DataLayout = DataLayout::NHWC;
Mike Kellye1d60bb2019-07-11 11:44:52 +0100327
328 // Determine whether padding is implicit or explicit
329 bool implicitPadding = operation.inputs.size() == 7 ||
330 (operation.inputs.size() >= 8 &&
331 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
332
333 if (implicitPadding)
334 {
335 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
336 }
337 else if (operation.inputs.size() >= 10)
338 {
339 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
340 }
341
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100342 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
Mike Kellye1d60bb2019-07-11 11:44:52 +0100343
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100344 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100345 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
346 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
347 // the DataLayout is NCHW
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100348 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
Mike Kellye1d60bb2019-07-11 11:44:52 +0100349 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
350 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100351 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100352 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100353
354 if (!weightsPin.IsValid())
355 {
356 return Fail("%s: Operation has invalid weights", __func__);
357 }
358
359 if (!biasPin.IsValid())
360 {
361 return Fail("%s: Operation has invalid biases", __func__);
362 }
363
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100364 ConstTensor weights = weightsPin.GetConstTensor();
365 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100366 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
367
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100368 ActivationFn activation;
369
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100370 if (implicitPadding)
371 {
372 android::nn::PaddingScheme paddingScheme;
373 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
374 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
375 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
376 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
377 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
378 {
379 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
380 }
381
Mike Kellye1d60bb2019-07-11 11:44:52 +0100382 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
383 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
384 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
385 const uint32_t kernelX = weights.GetShape()[widthIndex];
386 const uint32_t kernelY = weights.GetShape()[heightIndex];
387 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
388 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100389
Mike Kelly86b36d42019-07-12 16:39:33 +0100390 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
391 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100392
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100393 }
394 else if (operation.inputs.size() >= 10)
395 {
396 // explicit padding
397 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
398 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
399 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
400 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
401 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
402 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
403 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
404 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
405 {
406 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
407 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100408 }
409 else
410 {
411 return Fail("%s: Unsupported number of operation inputs", __func__);
412 }
413
414 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100415 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100416
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100417 bool isSupported = false;
418 FORWARD_LAYER_SUPPORT_FUNC(__func__,
419 IsConvolution2dSupported,
420 data.m_Backends,
421 isSupported,
422 inputInfo,
423 outputInfo,
424 desc,
425 weights.GetInfo(),
426 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100427
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100428 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100429 {
430 return false;
431 }
432
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100433 IConnectableLayer* startLayer =
434 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100435
436 if (!startLayer)
437 {
438 return Fail("%s: AddConvolution2dLayer failed", __func__);
439 }
440
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100441 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100442
443 if (!endLayer)
444 {
445 return Fail("%s: ProcessActivation failed", __func__);
446 }
447
448 input.Connect(startLayer->GetInputSlot(0));
449
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100450 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100451}
452
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100453bool HalPolicy::ConvertDepthToSpace(const Operation& operation, const Model& model, ConversionData& data)
454{
455 ALOGV("hal_1_2::HalPolicy::ConvertDepthToSpace()");
456 return ::ConvertDepthToSpace<hal_1_2::HalPolicy>(operation, model, data);
457}
458
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100459bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
460{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100461 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
462
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100463 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
464
465 if (!input.IsValid())
466 {
467 return Fail("%s: Operation has invalid inputs", __func__);
468 }
469
470 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
471
472 if (!output)
473 {
474 return Fail("%s: Could not read output 0", __func__);
475 }
476
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100477 const TensorInfo& inputInfo = input.GetTensorInfo();
478 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100479
480 if (IsDynamicTensor(outputInfo))
481 {
482 return Fail("%s: Dynamic output tensors are not supported", __func__);
483 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100484
485 // ArmNN does not currently support non-fixed weights or bias
486 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
487 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
488
489 if (weightsOperand == nullptr)
490 {
491 return Fail("%s: Operand is invalid", __func__);
492 }
Teresa Charlin3b959602019-10-31 17:05:47 +0000493 if ( weightsOperand->dimensions[0] != 1)
494 {
495 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
496 __func__, weightsOperand->dimensions[0] );
497 }
498
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100499 DepthwiseConvolution2dDescriptor desc;
500 desc.m_DataLayout = DataLayout::NHWC;
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100501
502 // Determine whether padding is implicit or explicit
503 bool implicitPadding = operation.inputs.size() == 8 ||
504 (operation.inputs.size() >= 9 &&
505 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
506
507 // Look ahead to find the optional DataLayout, if present
508 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
509 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
510
511 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
512 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
513 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
514 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
515
516 // Reinterpret weight data as [ H, W, I, M ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100517 TensorShape weightsShape({ weightsOperand->dimensions[1],
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100518 weightsOperand->dimensions[2],
519 inputInfo.GetShape()[channelsIndex],
520 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
521
522 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100523 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100524
525 const ConstTensorPin weightsPin =
526 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
527 1,
528 model,
529 data,
530 HWIMToMIHW,
531 &weightsShape);
532
533 // Bias is a 1D tensor
534 const ConstTensorPin biasPin =
535 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
536
537 if (!weightsPin.IsValid())
538 {
539 return Fail("%s: Operation has invalid weights", __func__);
540 }
541
542 if (!biasPin.IsValid())
543 {
544 return Fail("%s: Operation has invalid biases", __func__);
545 }
546
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100547 ConstTensor weights = weightsPin.GetConstTensor();
548 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100549 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
550
551 ActivationFn activation;
552
553 if (implicitPadding)
554 {
555 android::nn::PaddingScheme paddingScheme;
556 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
557 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
558 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
559 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
560 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
561 {
562 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
563 }
564
565 const uint32_t kernelX = weights.GetShape()[3];
566 const uint32_t kernelY = weights.GetShape()[2];
567 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
568 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
569
Mike Kelly86b36d42019-07-12 16:39:33 +0100570 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
571 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100572 }
573 else if (operation.inputs.size() >= 11)
574 {
575 // explicit padding
576 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
577 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
578 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
579 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
580 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
581 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
582 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
583 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
584 {
585 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
586 }
587 }
588 else
589 {
590 return Fail("%s: Unsupported number of operation inputs", __func__);
591 }
592
593 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100594 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100595
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100596 bool isSupported = false;
597 FORWARD_LAYER_SUPPORT_FUNC(__func__,
598 IsDepthwiseConvolutionSupported,
599 data.m_Backends,
600 isSupported,
601 inputInfo,
602 outputInfo,
603 desc,
604 weights.GetInfo(),
605 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100606
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100607 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100608 {
609 return false;
610 }
611
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100612 IConnectableLayer* startLayer =
613 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100614
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100615 if (!startLayer)
616 {
617 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
618 }
619
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100620 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100621 if (!endLayer)
622 {
623 return Fail("%s: ProcessActivation failed", __func__);
624 }
625
626 input.Connect(startLayer->GetInputSlot(0));
627
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100628 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100629}
630
Mike Kelly46272802019-08-14 17:00:48 +0100631bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
632{
633 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000634
635 if (IsQSymmDequantizeForWeights(operation, model))
636 {
637 // NOTE: QSymm8 weights are dequantized internally by the driver,
638 // therefore this type of Dequantize is implicitly supported
639 return true;
640 }
641
Mike Kelly46272802019-08-14 17:00:48 +0100642 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
643}
644
645bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
646{
647 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
648 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
649}
650
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100651bool HalPolicy::ConvertExpandDims(const Operation& operation, const Model& model, ConversionData& data)
652{
653 ALOGV("hal_1_2::HalPolicy::ConvertExpandDims()");
654
655 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
656
657 if (!input.IsValid())
658 {
659 return Fail("%s: Operation has invalid input", __func__);
660 }
661
662 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
663 if (!output)
664 {
665 return Fail("%s: Operation has invalid output", __func__);
666 }
667
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100668 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100669 if (IsDynamicTensor(outputInfo))
670 {
671 return Fail("%s: Dynamic output tensors are not supported", __func__);
672 }
673
674 int32_t axis;
675 if (!GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, axis, model, data))
676 {
677 return Fail("%s: failed to get axis input value", __func__);
678 }
679
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100680 TensorShape targetShape;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100681
682 try
683 {
684 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
685 }
686 catch (const std::exception &e)
687 {
688 return Fail("%s: %s", __func__, e.what());
689 }
690
691 if (targetShape != outputInfo.GetShape())
692 {
693 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
694 }
695
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100696 ReshapeDescriptor reshapeDescriptor;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100697 reshapeDescriptor.m_TargetShape = targetShape;
698
699 bool isSupported = false;
700 FORWARD_LAYER_SUPPORT_FUNC(__func__,
701 IsReshapeSupported,
702 data.m_Backends,
703 isSupported,
704 input.GetTensorInfo(),
705 reshapeDescriptor);
706
707 if (!isSupported)
708 {
709 return false;
710 }
711
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100712 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100713 assert(layer != nullptr);
714 input.Connect(layer->GetInputSlot(0));
715
716 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
717}
718
Mike Kelly46272802019-08-14 17:00:48 +0100719bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
720{
721 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
722 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
723}
724
725bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
726{
727 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
728 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
729}
730
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100731bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
732{
733 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
734
735 //
736 // Parse data
737 //
738 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
739 if (!input.IsValid())
740 {
741 return Fail("%s: Operation has invalid inputs", __func__);
742 }
743 const TensorInfo& inputInfo = input.GetTensorInfo();
744
745 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
746 if (!output)
747 {
748 return Fail("%s: Could not read output 0", __func__);
749 }
750 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
751 if (IsDynamicTensor(outputInfo))
752 {
753 return Fail("%s: Dynamic output tensors are not supported", __func__);
754 }
755
756 // Look ahead to determine data layout
757 DataLayout dataLayout = DataLayout::NHWC;
758 if (operation.inputs.size() == 12)
759 {
760 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
761 }
762 else
763 {
764 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
765 }
766
767 // NOTE:
768 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
769 // but Arm NN expects the filter's height and width indices to match the input's height and
770 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
771 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
772 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
773 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
774 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
775 const ConstTensorPin biasesPin =
776 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
777 if (!weightsPin.IsValid() || !biasesPin.IsValid())
778 {
779 return Fail("%s: Operation has invalid inputs", __func__);
780 }
781
782 ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000783 ConstTensor biases = biasesPin.GetConstTensor();
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100784 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
785
786 const TensorShape& inputShape = inputInfo.GetShape();
787 const TensorShape& outputShape = outputInfo.GetShape();
788 const TensorShape& weightsShape = weights.GetShape();
789 const TensorShape& biasesShape = biases.GetShape();
790
791 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
792 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
793 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
794 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
795
796 Convolution2dDescriptor desc;
797 desc.m_DataLayout = dataLayout;
798 desc.m_BiasEnabled = true;
799
800 int numGroups;
801 ActivationFn activation;
802
803 if (operation.inputs.size() == 12)
804 {
805 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
806 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
807 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
808 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
809 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
810 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
811 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
812 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
813 {
814 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
815 }
816
817 }
818 else if (operation.inputs.size() == 9)
819 {
820 android::nn::PaddingScheme paddingScheme;
821 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
822 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
823 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
824 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
825 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
826 {
827 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
828 }
829
830 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
831 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
832
833 const uint32_t kernelX = weightsShape[widthIndex];
834 const uint32_t kernelY = weightsShape[heightIndex];
835
836 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
837 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
838 }
839 else
840 {
841 return Fail("%s: Unsupported number of operation inputs", __func__);
842 }
843
844 const unsigned int outputChannels = outputShape[channelsIndex];
845
846 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
847 const unsigned int channelMultiplier = outputChannels / numGroups;
848
849 //
850 // Validate all relevant inputs
851 //
852 if (numGroups <= 0)
853 {
854 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
855 }
856
857 if (outputChannels % numGroups != 0u)
858 {
859 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
860 }
861
862 //
863 // Set up Splitter layer
864 //
865 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
866 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
867
868 TensorInfo splitterOutputInfo(4,
869 splitterDimSizes,
870 inputInfo.GetDataType(),
871 inputInfo.GetQuantizationScale(),
872 inputInfo.GetQuantizationOffset());
873
874 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
875
876 ViewsDescriptor splitterDesc(numGroups);
877 for (unsigned int group = 0u; group < numGroups; ++group)
878 {
879 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
880 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
881 {
882 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
883 }
884 }
885
886 bool isSupported = false;
887 FORWARD_LAYER_SUPPORT_FUNC(__func__,
888 IsSplitterSupported,
889 data.m_Backends,
890 isSupported,
891 inputInfo,
892 splitterOutputInfos,
893 splitterDesc);
894 if (!isSupported)
895 {
896 return false;
897 }
898
899 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
900 if (!splitterLayer)
901 {
902 return Fail("%s: Failed to add SplitterLayer", __func__);
903 }
904
905 input.Connect(splitterLayer->GetInputSlot(0));
906 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
907 {
908 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
909 }
910
911 //
912 // Set up Convolution2d layers for each group
913 //
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000914
915 // Set up group tensor shapes
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100916 TensorShape groupInputShape(inputShape);
917 groupInputShape[channelsIndex] = channelsPerGroup;
918
919 TensorShape groupOutputShape(outputShape);
920 groupOutputShape[channelsIndex] = 1;
921
922 TensorShape groupWeightsShape(weightsShape);
923 groupWeightsShape[0] /= channelMultiplier * numGroups;
924
925 TensorShape groupBiasesShape({ 1 });
926
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000927 // Set up group tensor infos
928 TensorInfo groupInputInfo(inputInfo);
929 groupInputInfo.SetShape(groupInputShape);
930
931 const TensorInfo& weightsInfo = weights.GetInfo();
932 TensorInfo groupWeightsInfo(weightsInfo);
933 groupWeightsInfo.SetShape(groupWeightsShape);
934
935 const TensorInfo& biasesInfo = biases.GetInfo();
936 TensorInfo groupBiasesInfo(biasesInfo);
937 groupBiasesInfo.SetShape(groupBiasesShape);
938
939 TensorInfo groupOutputInfo(outputInfo);
940 groupOutputInfo.SetShape(groupOutputShape);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100941
942 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
943 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
944
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000945 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100946 for (unsigned int group = 0u; group < numGroups; ++group)
947 {
948 for (unsigned int m = 0u; m < channelMultiplier; ++m)
949 {
950 auto index = group * channelMultiplier + m;
951
952 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
953 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
954
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000955 if (weightsInfo.HasPerAxisQuantization())
956 {
957 // Extract per-axis quantization scales for group weights
958 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
959 groupWeightsInfo.SetQuantizationScales(
960 std::vector<float>(weightsQuantScales.begin() + index,
961 weightsQuantScales.begin() + index + groupWeightsShape[0]));
962
963 // Extract per-axis quantization scales for group biases
964 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
965 groupBiasesInfo.SetQuantizationScales(
966 std::vector<float>(biasesQuantScales.begin() + index,
967 biasesQuantScales.begin() + index + groupWeightsShape[0]));
968 }
969
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100970 // Extract weights and biases data for current group convolution
971 ConstTensor groupWeights(groupWeightsInfo,
972 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
973 weightsDataOffset));
974 ConstTensor groupBiases(groupBiasesInfo,
975 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
976 biasesDataOffset));
977
978 isSupported = false;
979 FORWARD_LAYER_SUPPORT_FUNC(__func__,
980 IsConvolution2dSupported,
981 data.m_Backends,
982 isSupported,
983 groupInputInfo,
984 groupOutputInfo,
985 desc,
986 groupWeightsInfo,
987 Optional<TensorInfo>(groupBiasesInfo));
988 if (!isSupported)
989 {
990 return false;
991 }
992
993 IConnectableLayer *convLayer =
994 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
995 if (!convLayer)
996 {
997 return Fail("%s: AddConvolution2dLayer failed", __func__);
998 }
999
1000 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1001 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1002
1003 convLayers[index] = convLayer;
1004 }
1005 }
1006
1007 //
1008 // Set up Concat layer
1009 //
1010 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
1011 for (unsigned int group = 0u; group < numGroups; ++group)
1012 {
1013 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1014 {
1015 auto index = group * channelMultiplier + m;
1016 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1017 concatDescriptor.SetConcatAxis(channelsIndex);
1018 }
1019 }
1020
1021 isSupported = false;
1022 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1023 IsConcatSupported,
1024 data.m_Backends,
1025 isSupported,
1026 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1027 outputInfo,
1028 concatDescriptor);
1029 if (!isSupported)
1030 {
1031 return false;
1032 }
1033
1034 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1035 if (!concatLayer)
1036 {
1037 return Fail("%s: AddConcatLayer failed", __func__);
1038 }
1039
1040 for (unsigned int group = 0u; group < numGroups; ++group)
1041 {
1042 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1043 {
1044 auto index = group * channelMultiplier + m;
1045 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1046 }
1047 }
1048 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1049
1050 //
1051 // Set up Activation layer (if it is set)
1052 //
1053 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
1054 if (!endLayer)
1055 {
1056 return Fail("%s: ProcessActivation failed", __func__);
1057 }
1058
1059 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1060}
1061
Aron Virginas-Tara2a73802019-10-09 15:30:40 +01001062bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
1063{
1064 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
1065
1066 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1067 if (!input.IsValid())
1068 {
1069 return Fail("%s: Operation has an invalid input 0", __func__);
1070 }
1071
1072 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1073 if (!output)
1074 {
1075 return Fail("%s: Operation has an invalid output", __func__);
1076 }
1077
1078 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1079 if (IsDynamicTensor(outputInfo))
1080 {
1081 return Fail("%s: Dynamic output tensors are not supported", __func__);
1082 }
1083
1084 // Determine data type of input tensor
1085 OperandType inputType;
1086 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1087 {
1088 return Fail("%s: Operation has invalid inputs", __func__);
1089 }
1090
1091 InstanceNormalizationDescriptor desc;
1092
1093 // Read gamma, beta & epsilon
1094 if (inputType == OperandType::TENSOR_FLOAT16)
1095 {
1096 Half fp16Gamma;
1097 Half fp16Beta;
1098 Half fp16Epsilon;
1099
1100 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1101 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1102 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1103 {
1104 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1105 }
1106
1107 desc.m_Gamma = static_cast<float>(fp16Gamma);
1108 desc.m_Beta = static_cast<float>(fp16Beta);
1109 desc.m_Eps = static_cast<float>(fp16Epsilon);
1110 }
1111 else if (inputType == OperandType::TENSOR_FLOAT32)
1112 {
1113 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1114 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1115 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1116 {
1117 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1118 }
1119 }
1120 else
1121 {
1122 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1123 }
1124
1125 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1126
1127 bool isSupported = false;
1128 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1129 IsInstanceNormalizationSupported,
1130 data.m_Backends,
1131 isSupported,
1132 input.GetTensorInfo(),
1133 outputInfo,
1134 desc);
1135 if (!isSupported)
1136 {
1137 return false;
1138 }
1139
1140 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1141 input.Connect(layer->GetInputSlot(0));
1142
1143 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1144}
1145
Mike Kelly46272802019-08-14 17:00:48 +01001146bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1147{
1148 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1149 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1150}
1151
Sadik Armagan15d63e22019-07-26 16:59:35 +01001152bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1153{
1154 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001155 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001156}
1157
Mike Kelly46272802019-08-14 17:00:48 +01001158bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1159 const Model& model,
1160 ConversionData& data)
1161{
1162 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1163 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1164}
1165
1166bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1167{
1168 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1169 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1170}
1171
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001172bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1173{
1174 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1175
1176 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1177 if (!input.IsValid())
1178 {
1179 return Fail("%s: Failed to read input 0", __func__);
1180 }
1181
1182 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1183 if (!output)
1184 {
1185 return Fail("%s: Failed to read output", __func__);
1186 }
1187
1188 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1189 if (IsDynamicTensor(outputInfo))
1190 {
1191 return Fail("%s: Dynamic output tensors are not supported", __func__);
1192 }
1193
1194 // Determine data type of input tensor
1195 OperandType inputType;
1196 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1197 {
1198 return Fail("%s: Operation has invalid inputs", __func__);
1199 }
1200
1201 LogSoftmaxDescriptor descriptor;
1202
1203 // Read beta
1204 if (inputType == OperandType::TENSOR_FLOAT16)
1205 {
1206 Half fp16Beta;
1207 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1208 {
1209 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1210 }
1211
1212 descriptor.m_Beta = static_cast<float>(fp16Beta);
1213 }
1214 else if (inputType == OperandType::TENSOR_FLOAT32)
1215 {
1216 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1217 {
1218 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1219 }
1220 }
1221 else
1222 {
1223 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1224 }
1225
1226 // Read axis
1227 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1228 {
1229 return Fail("%s: Failed to read input 2", __func__);
1230 }
1231
1232 bool isSupported = false;
1233 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1234 IsLogSoftmaxSupported,
1235 data.m_Backends,
1236 isSupported,
1237 input.GetTensorInfo(),
1238 outputInfo,
1239 descriptor);
1240 if (!isSupported)
1241 {
1242 return false;
1243 }
1244
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001245 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001246 if (!layer)
1247 {
1248 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1249 }
1250
1251 input.Connect(layer->GetInputSlot(0));
1252
1253 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1254}
1255
Sadik Armagan15d63e22019-07-26 16:59:35 +01001256bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1257{
1258 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001259 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001260}
1261
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001262bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1263{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001264 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1265
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001266 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1267 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1268
1269 if (!input0.IsValid() || !input1.IsValid())
1270 {
1271 return Fail("%s: Operation has invalid inputs", __func__);
1272 }
1273
1274 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1275 if (!outputOperand)
1276 {
1277 return Fail("%s: Could not read output", __func__);
1278 }
1279
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001280 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001281 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001282 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001283 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001284 }
1285
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001286 bool isSupported = false;
1287 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1288 IsMaximumSupported,
1289 data.m_Backends,
1290 isSupported,
1291 input0.GetTensorInfo(),
1292 input1.GetTensorInfo(),
1293 outInfo);
1294
1295 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001296 {
1297 return false;
1298 }
1299
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001300 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001301 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001302 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1303 if (!isReshapeSupported)
1304 {
1305 return false;
1306 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001307
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001308 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001309}
1310
Mike Kelly46272802019-08-14 17:00:48 +01001311bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1312{
1313 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1314 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1315}
1316
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001317bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1318{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001319 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1320
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001321 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1322 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1323
1324 if (!input0.IsValid() || !input1.IsValid())
1325 {
1326 return Fail("%s: Operation has invalid inputs", __func__);
1327 }
1328
1329 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1330 if (!output)
1331 {
1332 return Fail("%s: Could not read output 0", __func__);
1333 }
1334
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001335 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001336 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001337 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001338 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001339 }
1340
1341 bool isSupported = false;
1342 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1343 IsMinimumSupported,
1344 data.m_Backends,
1345 isSupported,
1346 input0.GetTensorInfo(),
1347 input1.GetTensorInfo(),
1348 outputInfo);
1349
1350 if (!isSupported)
1351 {
1352 return false;
1353 }
1354
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001355 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001356 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001357 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1358 if (!isReshapeSupported)
1359 {
1360 return false;
1361 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001362
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001363 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001364}
1365
Mike Kelly46272802019-08-14 17:00:48 +01001366bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1367{
1368 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1369 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1370}
1371
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001372bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1373{
1374 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1375 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1376}
1377
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001378bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1379{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001380 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1381
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001382 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1383 if (!input.IsValid())
1384 {
1385 return Fail("%s: Could not read input 0", __func__);
1386 }
1387
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001388 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1389 if (!output)
1390 {
1391 return Fail("%s: Could not read output", __func__);
1392 }
1393
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001394 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001395 unsigned int rank = inputInfo.GetNumDimensions();
1396
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001397 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001398 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1399 {
1400 return Fail("%s: Could not convert paddings", __func__);
1401 }
1402
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001403 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001404 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001405 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001406 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001407 }
1408
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001409 // Determine type of padding value
1410 OperandType operandType0;
1411 OperandType operandType2;
1412
1413 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1414 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1415 {
1416 return Fail("%s: Operation has invalid inputs", __func__);
1417 }
1418
1419 // Read value to use for padding
1420 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1421 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001422 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001423 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1424 {
1425 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1426 }
1427
1428 descriptor.m_PadValue = f16PadValue;
1429 }
1430 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1431 {
1432 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1433 {
1434 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1435 }
1436 }
1437 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1438 {
Mike Kelly3c673942019-07-25 09:26:06 +01001439 int32_t intPadValue = 0;
1440 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001441 {
1442 return Fail("%s: Could not read input 2 (INT32)", __func__);
1443 }
Mike Kelly3c673942019-07-25 09:26:06 +01001444 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001445 }
1446 else
1447 {
1448 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1449 }
1450
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001451 bool isSupported = false;
1452 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1453 IsPadSupported,
1454 data.m_Backends,
1455 isSupported,
1456 inputInfo,
1457 outputInfo,
1458 descriptor);
1459 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001460 {
1461 return false;
1462 }
1463
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001464 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001465 assert(layer != nullptr);
1466 input.Connect(layer->GetInputSlot(0));
1467 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1468
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001469 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001470}
1471
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001472bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1473{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001474 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1475
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001476 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1477 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1478
1479 if (!input.IsValid() || !alpha.IsValid())
1480 {
1481 return Fail("%s: Operation has invalid inputs", __func__);
1482 }
1483
1484 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1485
1486 if (!output)
1487 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001488 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001489 }
1490
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001491 const TensorInfo& inputInfo = input.GetTensorInfo();
1492 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1493 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001494
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001495 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001496 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001497 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001498 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001499
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001500 bool isSupported = false;
1501 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1502 IsPreluSupported,
1503 data.m_Backends,
1504 isSupported,
1505 inputInfo,
1506 alphaInfo,
1507 outputInfo);
1508 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001509 {
1510 return false;
1511 }
1512
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001513 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001514
1515 if (!layer)
1516 {
1517 return Fail("%s: AddPreluLayer failed", __func__);
1518 }
1519
Sadik Armagan64b19b52019-08-19 09:49:58 +01001520 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1521 if (!isReshapeSupported)
1522 {
1523 return false;
1524 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001525
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001526 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001527}
1528
Sadik Armagan5a476a82019-07-30 09:43:18 +01001529bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1530{
1531 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1532
1533 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1534 if (!input.IsValid())
1535 {
1536 return Fail("%s: Operation has invalid input", __func__);
1537 }
1538
1539 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1540 if (!outputOperand)
1541 {
1542 return Fail("%s: Operation has invalid outputs", __func__);
1543 }
1544
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001545 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001546 if (IsDynamicTensor(outputInfo))
1547 {
1548 return Fail("%s: Dynamic output tensors are not supported", __func__);
1549 }
1550
1551 bool isSupported = false;
1552 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1553 IsQuantizeSupported,
1554 data.m_Backends,
1555 isSupported,
1556 input.GetTensorInfo(),
1557 outputInfo);
1558 if (!isSupported)
1559 {
1560 return false;
1561 }
1562
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001563 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001564 assert(layer != nullptr);
1565 input.Connect(layer->GetInputSlot(0));
1566
1567 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1568}
1569
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001570bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1571{
1572 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1573
1574 //Inputs:
1575 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1576 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1577 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1578 if (!input.IsValid())
1579 {
1580 return Fail("%s: Could not read input 0: input", __func__);
1581 }
1582
1583 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1584 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1585 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1586 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1587 if (!previousCellStateIn.IsValid())
1588 {
1589 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1590 }
1591
1592 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1593 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1594 // is quantized with a fixed quantization range of -1, 127/128.
1595 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1596 if (!previousOutputIn.IsValid())
1597 {
1598 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1599 }
1600
1601 // Get the input tensors:
1602 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1603 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1604 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1605 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001606 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001607
1608 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1609 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1610 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1611 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001612 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001613
1614 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1615 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1616 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1617 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001618 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001619
1620 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1621 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1622 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1623 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001624 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001625
1626 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1627 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1628 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1629 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001630 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001631
1632 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1633 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1634 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1635 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001636 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001637
1638 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1639 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1640 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1641 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001642 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001643
1644 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1645 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1646 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1647 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001648 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001649
1650 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1651 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1652 // of input and weights scales and zeroPoint equal to 0.
1653 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001654 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001655
1656 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1657 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1658 // of input and weights scales and zeroPoint equal to 0.
1659 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001660 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001661
1662 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1663 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1664 // and weights scales and zeroPoint equal to 0.
1665 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001666 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001667
1668 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1669 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1670 // of input and weights scales and zeroPoint equal to 0.
1671 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001672 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001673
1674 if (!inputToInputWeightsPin.IsValid() ||
1675 !inputToForgetWeightsPin.IsValid() ||
1676 !inputToCellWeightsPin.IsValid() ||
1677 !inputToOutputWeightsPin.IsValid() ||
1678 !recurrentToInputWeightsPin.IsValid() ||
1679 !recurrentToForgetWeightsPin.IsValid() ||
1680 !recurrentToCellWeightsPin.IsValid() ||
1681 !recurrentToOutputWeightsPin.IsValid() ||
1682 !inputGateBiasPin.IsValid() ||
1683 !forgetGateBiasPin.IsValid() ||
1684 !cellBiasPin.IsValid() ||
1685 !outputGateBiasPin.IsValid())
1686 {
1687 return Fail("%s: Operation has invalid tensor inputs", __func__);
1688 }
1689
1690 // Outputs:
1691 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1692 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1693 // of -2^4, 2^4 * 32767/32768.
1694 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1695 if (!cellStateOut)
1696 {
1697 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1698 }
1699
1700 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1701 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1702 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1703 if (!output)
1704 {
1705 return Fail("%s: Could not read output 1: output", __func__);
1706 }
1707
1708 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001709 const TensorInfo& inputInfo = input.GetTensorInfo();
1710 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1711 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001712
1713 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001714 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1715 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001716
1717 // Dynamic tensors currently not supported
1718 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1719 {
1720 return Fail("%s: Dynamic output tensors are not supported", __func__);
1721 }
1722
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001723 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001724
1725 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1726 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1727 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1728 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1729 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1730 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1731 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1732 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1733 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1734 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1735 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1736 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1737
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001738 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001739 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1740 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1741 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1742 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1743 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1744 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1745 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1746 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1747 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1748 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1749 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1750 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1751
1752 bool isSupported = false;
1753 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1754 IsQuantizedLstmSupported,
1755 data.m_Backends,
1756 isSupported,
1757 inputInfo,
1758 previousCellStateInInfo,
1759 previousOutputInInfo,
1760 cellStateOutInfo,
1761 outputInfo,
1762 paramsInfo);
1763
1764 if (!isSupported)
1765 {
1766 return false;
1767 }
1768
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001769 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001770 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001771 previousCellStateIn.Connect(layer->GetInputSlot(1));
1772 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001773
1774 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1775 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1776}
1777
Sadik Armagan61113162019-07-25 09:09:40 +01001778bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1779{
1780 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1781 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1782}
1783
1784bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1785{
1786 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1787 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1788}
1789
1790bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1791{
1792 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1793 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1794}
1795
Mike Kelly46272802019-08-14 17:00:48 +01001796bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1797{
1798 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1799 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1800}
1801
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001802bool HalPolicy::ConvertResize(const Operation& operation,
1803 const Model& model,
1804 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001805 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001806{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001807 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001808 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001809
1810 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001811 if (!input.IsValid())
1812 {
1813 return Fail("%s: Could not read input 0", __func__);
1814 }
1815
1816 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1817 if (!output)
1818 {
1819 return Fail("%s: Could not read output 0", __func__);
1820 }
1821
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001822 const TensorInfo& inputInfo = input.GetTensorInfo();
1823 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001824
1825 if (IsDynamicTensor(outputInfo))
1826 {
1827 return Fail("%s: Dynamic output tensors are not supported", __func__);
1828 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001829
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001830 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001831 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001832 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1833
1834 OperandType operandType1;
1835 OperandType operandType2;
1836
1837 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1838 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1839 {
1840 return Fail("%s: Operation has invalid inputs", __func__);
1841 }
1842
1843 if (operandType1 != operandType2)
1844 {
1845 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1846 }
1847
1848 if (operandType1 == OperandType::INT32)
1849 {
1850 // Case 1: resizing by shape
1851 int32_t targetWidth = 0;
1852 int32_t targetHeight = 0;
1853
1854 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1855 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1856 {
1857 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1858 }
1859
1860 if (targetWidth < 0 || targetHeight < 0)
1861 {
1862 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1863 "Target width/height cannot be < 0", __func__);
1864 }
1865
1866 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001867 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001868 }
1869 else if (operandType1 == OperandType::FLOAT32)
1870 {
1871 // Case 2: resizing by scale
1872 float widthScale = 1.0f;
1873 float heightScale = 1.0f;
1874
1875 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1876 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1877 {
1878 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1879 }
1880
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001881 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001882 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1883
1884 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1885 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1886
1887 descriptor.m_TargetWidth = std::floor(width * widthScale);
1888 descriptor.m_TargetHeight = std::floor(height * heightScale);
1889 }
Keith Davisd410b602019-12-19 12:31:30 +00001890 else if (operandType1 == OperandType::FLOAT16)
1891 {
1892 Half widthScale;
1893 Half heightScale;
1894
1895 if (!GetInputScalar<HalPolicy>(operation, 1, HalPolicy::OperandType::FLOAT16, widthScale, model, data) ||
1896 !GetInputScalar<HalPolicy>(operation, 2, HalPolicy::OperandType::FLOAT16, heightScale, model, data))
1897 {
1898 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1899 }
1900
1901 const TensorShape& inputShape = inputInfo.GetShape();
1902 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1903
1904 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
1905 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
1906
1907 descriptor.m_TargetWidth = std::floor(width * widthScale);
1908 descriptor.m_TargetHeight = std::floor(height * heightScale);
1909 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001910 else
1911 {
Keith Davisd410b602019-12-19 12:31:30 +00001912 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001913 }
1914
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001915 bool isSupported = false;
1916 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1917 IsResizeSupported,
1918 data.m_Backends,
1919 isSupported,
1920 inputInfo,
1921 outputInfo,
1922 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001923
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001924 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001925 {
1926 return false;
1927 }
1928
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001929 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001930
1931 assert(layer != nullptr);
1932
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001933 input.Connect(layer->GetInputSlot(0));
1934
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001935 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001936}
1937
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001938bool HalPolicy::ConvertRsqrt(const Operation& operation, const Model& model, ConversionData& data)
1939{
1940 ALOGV("hal_1_2::HalPolicy::ConvertRsqrt()");
1941
1942 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1943 if (!input.IsValid())
1944 {
1945 return Fail("%s: Operation has invalid input", __func__);
1946 }
1947
1948 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1949 if (!output)
1950 {
1951 return Fail("%s: Could not read output 0", __func__);
1952 }
1953
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001954 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001955 if (IsDynamicTensor(outputInfo))
1956 {
1957 return Fail("%s: Dynamic output tensors are not supported", __func__);
1958 }
1959
1960 bool isSupported = false;
1961 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1962 IsRsqrtSupported,
1963 data.m_Backends,
1964 isSupported,
1965 input.GetTensorInfo(),
1966 outputInfo);
1967
1968 if (!isSupported)
1969 {
1970 return false;
1971 }
1972
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001973 IConnectableLayer* const layer = data.m_Network->AddRsqrtLayer();
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001974 assert(layer != nullptr);
1975 input.Connect(layer->GetInputSlot(0));
1976
1977 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1978}
1979
Finn Williamsd74c5052019-07-30 17:06:00 +01001980bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1981{
1982 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1983 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1984}
1985
Keith Davisa6bc52f2019-06-26 09:39:49 +01001986bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1987{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001988 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001989
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001990 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001991 if (!input.IsValid() )
1992 {
1993 return Fail("%s: Operation has invalid inputs", __func__);
1994 }
1995
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001996 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001997 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001998 if (rank != 4)
1999 {
2000 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2001 }
2002
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002003 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2004 if (!output)
2005 {
2006 return Fail("%s: Could not read output 0", __func__);
2007 }
2008
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002009 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002010 if (IsDynamicTensor(outputInfo))
2011 {
2012 return Fail("%s: Dynamic output tensors are not supported", __func__);
2013 }
2014
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002015 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01002016
2017 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
2018
2019 if (desc.m_BlockSize <= 1)
2020 {
2021 return Fail("%s: Block size must be at least 1 in all dimensions");
2022 }
2023
2024 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
2025
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002026 bool isSupported = false;
2027 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2028 IsSpaceToDepthSupported,
2029 data.m_Backends,
2030 isSupported,
2031 inputInfo,
2032 outputInfo,
2033 desc);
2034 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01002035 {
2036 return false;
2037 }
2038
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002039 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002040 assert(layer != nullptr);
2041 input.Connect(layer->GetInputSlot(0));
2042
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002043 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002044}
2045
Francis Murtagh074c25a2019-07-22 16:40:57 +01002046bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
2047{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002048 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
2049
Francis Murtagh074c25a2019-07-22 16:40:57 +01002050 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2051 if (!input.IsValid())
2052 {
2053 return Fail("%s: Operation has invalid inputs", __func__);
2054 }
2055
2056 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2057 if (!outputOperand)
2058 {
2059 return Fail("%s: Operation has no outputs", __func__);
2060 }
2061
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002062 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01002063 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01002064 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002065 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002066 }
2067
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002068 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01002069 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
2070 {
2071 return Fail("%s: Operation has invalid inputs", __func__);
2072 }
2073
2074 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
2075 2,
2076 HalPolicy::OperandType::INT32,
2077 desc.m_Axis,
2078 model,
2079 data))
2080 {
2081 return Fail("%s: Operation has invalid inputs", __func__);
2082 }
2083
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01002084 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2085 !(desc.m_Axis == 1 ||
2086 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2087 {
2088 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2089 }
2090
Francis Murtagh074c25a2019-07-22 16:40:57 +01002091 bool isSupported = false;
2092 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2093 IsSoftmaxSupported,
2094 data.m_Backends,
2095 isSupported,
2096 input.GetTensorInfo(),
2097 outputInfo,
2098 desc);
2099 if (!isSupported)
2100 {
2101 return false;
2102 }
2103
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002104 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002105 assert(layer != nullptr);
2106 input.Connect(layer->GetInputSlot(0));
2107
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002108 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002109}
2110
Mike Kelly0a879362019-07-29 16:56:31 +01002111bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2112{
2113 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2114 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2115}
2116
Sadik Armagan61113162019-07-25 09:09:40 +01002117bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2118{
2119 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2120 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2121}
2122
Pablo Tello972603f2019-11-28 15:21:41 +00002123template<typename HalPolicy,
2124 typename HalOperation = typename HalPolicy::Operation,
2125 typename HalModel = typename HalPolicy::Model>
2126bool SetupAndTrackLayerOutputSlotAndOverrideTensorInfo(const HalOperation& operation,
2127 uint32_t operationOutputIndex,
2128 armnn::IConnectableLayer& layer,
2129 uint32_t layerOutputIndex,
2130 const HalModel& model,
2131 ConversionData& data,
2132 const armnn::TensorInfo tensor_info)
2133{
2134 using HalOperand = typename HalPolicy::Operand;
2135
2136 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
2137 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
2138 {
2139 return false;
2140 }
2141
2142 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
2143
2144 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
2145 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
2146
2147 outputSlot.SetTensorInfo(tensor_info);
2148
2149 return true;
2150}
2151
2152
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002153bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2154{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002155 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2156
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002157 // Inputs:
2158 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2159 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2160 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2161 if (!input.IsValid())
2162 {
2163 return Fail("%s: Could not read input 0: input", __func__);
2164 }
2165 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2166 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2167 if (!outputStateIn.IsValid())
2168 {
2169 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2170 }
2171 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2172 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2173 if (!cellStateIn.IsValid())
2174 {
2175 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2176 }
2177
2178 // Get the mandatory input tensors:
2179 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2180 // [num_units, input_size].
2181 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002182 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002183 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2184 // [num_units, input_size].
2185 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002186 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002187 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2188 // [num_units, input_size].
2189 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002190 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002191 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2192 // [num_units, output_size].
2193 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002194 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002195 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2196 // [num_units, output_size].
2197 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002198 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002199 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2200 // [num_units, output_size].
2201 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002202 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002203 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2204 const ConstTensorPin forgetGateBiasPin =
2205 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2206 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2207 const ConstTensorPin cellBiasPin =
2208 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2209 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2210 const ConstTensorPin outputGateBiasPin =
2211 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2212
2213 if (!inputToForgetWeightsPin.IsValid() ||
2214 !inputToCellWeightsPin.IsValid() ||
2215 !inputToOutputWeightsPin.IsValid() ||
2216 !recurrentToForgetWeightsPin.IsValid() ||
2217 !recurrentToCellWeightsPin.IsValid() ||
2218 !recurrentToOutputWeightsPin.IsValid() ||
2219 !forgetGateBiasPin.IsValid() ||
2220 !cellBiasPin.IsValid() ||
2221 !outputGateBiasPin.IsValid())
2222 {
2223 return Fail("%s: Operation has invalid tensor inputs", __func__);
2224 }
2225
2226 // Get the optional input tensors:
2227 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2228 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2229 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002230 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002231 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2232 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2233 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2234 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002235 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002236 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2237 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002238 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002239 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2240 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002241 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002242 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2243 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002244 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002245 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2246 const ConstTensorPin inputGateBiasPin =
2247 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2248 12,
2249 model,
2250 data,
2251 g_DontPermute,
2252 nullptr,
2253 true);
2254
2255 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2256 // [output_size, num_units].
2257 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002258 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002259 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2260 const ConstTensorPin projectionBiasPin =
2261 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2262 17,
2263 model,
2264 data,
2265 g_DontPermute,
2266 nullptr,
2267 true);
2268
2269 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2270 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2271 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2272 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2273 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2274 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2275 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2276 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2277 {
2278 return Fail("%s: Operation has invalid tensor inputs", __func__);
2279 }
2280
2281 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2282 // 20: The activation function: A value indicating the activation function:
2283 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2284 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2285 // If set to 0.0 then clipping is disabled.
2286 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2287 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2288 ActivationFn activation;
2289 float cellClip;
2290 float projClip;
2291 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2292 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2293 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2294 {
2295 return Fail("%s: Operation has invalid scalar inputs", __func__);
2296 }
2297
2298 // Get the normalization tensors
2299 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2300 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002301 const ConstTensorPin inputLayerNormWeightsPin
2302 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002303
2304 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2305 // Used to rescale normalized inputs to activation at forget gate.
2306 const ConstTensorPin forgetLayerNormWeightsPin =
2307 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2308 24,
2309 model,
2310 data,
2311 g_DontPermute,
2312 nullptr,
2313 true);
2314
2315 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2316 // Used to rescale normalized inputs to activation at cell gate.
2317 const ConstTensorPin cellLayerNormWeightsPin =
2318 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2319 25,
2320 model,
2321 data,
2322 g_DontPermute,
2323 nullptr,
2324 true);
2325
2326 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2327 // Used to rescale normalized inputs to activation at output gate.
2328 const ConstTensorPin outputLayerNormWeightsPin =
2329 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2330 26,
2331 model,
2332 data,
2333 g_DontPermute,
2334 nullptr,
2335 true);
2336
2337 // Outputs:
2338 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2339 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2340 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2341 if (!scratchBuffer)
2342 {
2343 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2344 }
2345 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2346 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2347 if (!outputStateOut)
2348 {
2349 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2350 }
2351 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2352 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2353 if (!cellStateOut)
2354 {
2355 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2356 }
2357 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2358 // effectively the same as the current “output state (out)” value.
2359 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2360 if (!output)
2361 {
2362 return Fail("%s: Could not read output 3: output", __func__);
2363 }
2364
2365 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002366 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002367 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2368 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2369 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2370 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2371 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2372 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2373 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2374 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2375 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2376 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2377 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2378 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2379 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2380 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2381 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2382 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2383 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2384 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2385 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2386 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2387 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2388
2389 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002390 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002391 desc.m_ActivationFunc = activation;
2392 desc.m_ClippingThresCell = cellClip;
2393 desc.m_ClippingThresProj = projClip;
2394 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2395 params.m_RecurrentToInputWeights == nullptr ||
2396 params.m_InputGateBias == nullptr);
2397 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2398 params.m_CellToOutputWeights != nullptr);
2399 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2400 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2401 params.m_ForgetLayerNormWeights != nullptr ||
2402 params.m_CellLayerNormWeights != nullptr ||
2403 params.m_OutputLayerNormWeights != nullptr);
2404
2405 // validate the optional input groups
2406 if (desc.m_CifgEnabled &&
2407 (params.m_InputToInputWeights != nullptr ||
2408 params.m_RecurrentToInputWeights != nullptr ||
2409 params.m_InputGateBias != nullptr))
2410 {
2411 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2412 " and input gate bias must be provided", __func__);
2413 }
2414
2415 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2416 {
2417 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2418 }
2419
2420 if (desc.m_PeepholeEnabled &&
2421 (params.m_CellToForgetWeights == nullptr ||
2422 params.m_CellToOutputWeights == nullptr ||
2423 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2424 {
2425 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2426 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2427 }
2428
2429 if (desc.m_LayerNormEnabled &&
2430 (params.m_ForgetLayerNormWeights == nullptr ||
2431 params.m_CellLayerNormWeights == nullptr ||
2432 params.m_OutputLayerNormWeights == nullptr ||
2433 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2434 {
2435 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2436 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2437 }
2438
2439 // Check if the layer is supported
2440 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002441 const TensorInfo& inputInfo = input.GetTensorInfo();
2442 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2443 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002444
2445 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002446 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2447 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2448 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2449 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002450
Pablo Tello972603f2019-11-28 15:21:41 +00002451 // Check if the scratch buffer shape was initialized,
2452 // In some cases the shape could be (0,0) which requires the driver
2453 // to infer the shape and set it up accordingly.
2454 // The code below does that.
2455 TensorInfo fixSbInfo = scratchBufferInfo;
2456 if (IsDynamicTensor(scratchBufferInfo))
2457 {
2458 auto & s = fixSbInfo.GetShape();
2459 s[0] = outputStateInInfo.GetShape()[0];
2460 if (desc.m_CifgEnabled)
2461 {
2462 // 2D tensor with dimensions [num_units * 3, batch_size] with CIFG
2463 s[1] = cellStateOutInfo.GetShape()[1]*3;
2464 }
2465 else
2466 {
2467 // scratch_buffer [num_units * 4, batch_size] without CIFG
2468 s[1] = cellStateOutInfo.GetShape()[1]*4;
2469 }
2470 }
2471
2472 if (IsDynamicTensor(outputStateOutInfo) ||
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002473 IsDynamicTensor(cellStateOutInfo) ||
2474 IsDynamicTensor(outputInfo))
2475 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002476 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2477 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2478 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002479 }
2480
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002481 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002482 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002483 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2484 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2485 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2486 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2487 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2488 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2489 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2490 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2491 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2492
2493 // Optional parameters
2494 if(!desc.m_CifgEnabled)
2495 {
2496 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2497 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2498 if (params.m_CellToInputWeights != nullptr)
2499 {
2500 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2501 }
2502 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2503 }
2504
2505 if(desc.m_ProjectionEnabled)
2506 {
2507 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2508 if (params.m_ProjectionBias != nullptr)
2509 {
2510 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2511 }
2512 }
2513
2514 if(desc.m_PeepholeEnabled)
2515 {
2516 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2517 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2518 }
2519
2520 if (desc.m_LayerNormEnabled)
2521 {
2522 if(!desc.m_CifgEnabled)
2523 {
2524 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2525 }
2526 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2527 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2528 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2529 }
2530
2531 bool isSupported = false;
2532 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2533 IsLstmSupported,
2534 data.m_Backends,
2535 isSupported,
2536 inputInfo,
2537 outputStateInInfo,
2538 cellStateInInfo,
Pablo Tello972603f2019-11-28 15:21:41 +00002539 fixSbInfo,
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002540 outputStateOutInfo,
2541 cellStateOutInfo,
2542 outputInfo,
2543 desc,
2544 paramsInfo);
2545 if (!isSupported)
2546 {
2547 return false;
2548 }
2549
2550 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002551 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002552
2553 input.Connect(layer->GetInputSlot(0));
2554 outputStateIn.Connect(layer->GetInputSlot(1));
2555 cellStateIn.Connect(layer->GetInputSlot(2));
2556
Pablo Tello972603f2019-11-28 15:21:41 +00002557
2558 return (
2559 (IsDynamicTensor(scratchBufferInfo)?
2560 SetupAndTrackLayerOutputSlotAndOverrideTensorInfo<hal_1_2::HalPolicy>(
2561 operation, 0, *layer, 0, model, data,fixSbInfo):
2562 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(
2563 operation, 0, *layer, 0, model, data)) &&
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002564 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2565 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2566 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2567}
2568
Sadik Armagan701d9a02019-09-04 15:16:18 +01002569bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2570{
2571 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002572 ActivationDescriptor desc;
2573 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002574
2575 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2576}
2577
Mike Kelly46272802019-08-14 17:00:48 +01002578bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2579{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002580 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002581 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2582}
2583
2584bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2585{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002586 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002587 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2588}
2589
2590bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2591{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002592 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002593 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2594}
2595
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002596bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002597{
2598 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2599
2600 if (!input.IsValid())
2601 {
2602 return Fail("%s: Operation has invalid inputs", __func__);
2603 }
2604
2605 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2606
2607 if (!output)
2608 {
2609 return Fail("%s: Could not read output 0", __func__);
2610 }
2611
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002612 const TensorInfo& inputInfo = input.GetTensorInfo();
2613 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002614 if (IsDynamicTensor(outputInfo))
2615 {
2616 return Fail("%s: Dynamic output tensors are not supported", __func__);
2617 }
2618
2619 // ArmNN does not currently support non-fixed weights or bias
2620 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2621 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2622
2623 if (weightsOperand == nullptr)
2624 {
2625 return Fail("%s: Operand is invalid", __func__);
2626 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002627 TransposeConvolution2dDescriptor desc;
2628 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002629
2630 // Determine whether padding is implicit or explicit
2631 bool implicitPadding = operation.inputs.size() == 9;
2632
2633 if (implicitPadding )
2634 {
2635 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2636 }
2637 else
2638 {
2639 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2640 }
2641
2642 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2643 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2644 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2645
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002646 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002647
2648 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2649 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002650 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002651 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2652 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2653
2654 // Bias is a 1D tensor
2655 const ConstTensorPin biasPin =
2656 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2657
2658 if (!weightsPin.IsValid())
2659 {
2660 return Fail("%s: Operation has invalid weights", __func__);
2661 }
2662
2663 if (!biasPin.IsValid())
2664 {
2665 return Fail("%s: Operation has invalid biases", __func__);
2666 }
2667
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002668 ConstTensor weights = weightsPin.GetConstTensor();
2669 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002670 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2671
2672 ActivationFn activation;
2673
2674 if (implicitPadding)
2675 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002676 int32_t strideX{0};
2677 int32_t strideY{0};
2678 int32_t padLeft{0};
2679 int32_t padRight{0};
2680 int32_t padTop{0};
2681 int32_t padBottom{0};
2682
David Monahan613b49c2019-06-27 11:37:47 +01002683 android::nn::PaddingScheme paddingScheme;
2684 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002685 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2686 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002687 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2688 {
2689 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2690 }
2691
2692 const uint32_t kernelX = weights.GetShape()[widthIndex];
2693 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002694 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2695 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002696
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002697 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2698 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2699
2700 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2701 // but Arm NN only supports values >= 0
2702 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2703 {
2704 return Fail("%s: Negative padding values are not supported", __func__);
2705 }
2706
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002707 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2708 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002709 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2710 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2711 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2712 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002713 }
2714 else if (operation.inputs.size() == 11)
2715 {
2716 // explicit padding
2717 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2718 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2719 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2720 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2721 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2722 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2723 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2724 {
2725 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2726 }
2727 }
2728 else
2729 {
2730 return Fail("%s: Unsupported number of operation inputs", __func__);
2731 }
2732
2733 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002734 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002735
2736 bool isSupported = false;
2737 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2738 IsTransposeConvolution2dSupported,
2739 data.m_Backends,
2740 isSupported,
2741 inputInfo,
2742 outputInfo,
2743 desc,
2744 weights.GetInfo(),
2745 biases);
2746 if (!isSupported)
2747 {
2748 return false;
2749 }
2750
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002751 IConnectableLayer* startLayer =
2752 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002753 if (!startLayer)
2754 {
2755 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2756 }
2757
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002758 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002759 if (!endLayer)
2760 {
2761 return Fail("%s: ProcessActivation failed", __func__);
2762 }
2763
2764 input.Connect(startLayer->GetInputSlot(0));
2765
2766 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2767}
2768
Mike Kellyb5fdf382019-06-11 16:35:25 +01002769} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002770} // namespace armnn_driver