blob: e6f8acbb6fa2f8b6d7f6caf6edf4b417e0b7928d [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"
7
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01008#include "Utils.hpp"
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01009
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010010#include <DataLayoutIndexed.hpp>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +010011#include <Half.hpp>
Narumol Prangnawarat85f96542019-09-12 16:26:29 +010012#include <TensorUtils.hpp>
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010013
Teresa Charlin8f6429d2019-10-01 13:10:15 +010014#include <armnn/TypesUtils.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 }
1890 else
1891 {
1892 // NOTE: FLOAT16 scales are not supported
1893 return false;
1894 }
1895
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001896 bool isSupported = false;
1897 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1898 IsResizeSupported,
1899 data.m_Backends,
1900 isSupported,
1901 inputInfo,
1902 outputInfo,
1903 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001904
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001905 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001906 {
1907 return false;
1908 }
1909
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001910 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001911
1912 assert(layer != nullptr);
1913
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001914 input.Connect(layer->GetInputSlot(0));
1915
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001916 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001917}
1918
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001919bool HalPolicy::ConvertRsqrt(const Operation& operation, const Model& model, ConversionData& data)
1920{
1921 ALOGV("hal_1_2::HalPolicy::ConvertRsqrt()");
1922
1923 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1924 if (!input.IsValid())
1925 {
1926 return Fail("%s: Operation has invalid input", __func__);
1927 }
1928
1929 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1930 if (!output)
1931 {
1932 return Fail("%s: Could not read output 0", __func__);
1933 }
1934
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001935 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001936 if (IsDynamicTensor(outputInfo))
1937 {
1938 return Fail("%s: Dynamic output tensors are not supported", __func__);
1939 }
1940
1941 bool isSupported = false;
1942 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1943 IsRsqrtSupported,
1944 data.m_Backends,
1945 isSupported,
1946 input.GetTensorInfo(),
1947 outputInfo);
1948
1949 if (!isSupported)
1950 {
1951 return false;
1952 }
1953
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001954 IConnectableLayer* const layer = data.m_Network->AddRsqrtLayer();
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001955 assert(layer != nullptr);
1956 input.Connect(layer->GetInputSlot(0));
1957
1958 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1959}
1960
Finn Williamsd74c5052019-07-30 17:06:00 +01001961bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1962{
1963 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1964 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1965}
1966
Keith Davisa6bc52f2019-06-26 09:39:49 +01001967bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1968{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001969 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001970
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001971 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001972 if (!input.IsValid() )
1973 {
1974 return Fail("%s: Operation has invalid inputs", __func__);
1975 }
1976
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001977 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001978 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001979 if (rank != 4)
1980 {
1981 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1982 }
1983
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001984 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1985 if (!output)
1986 {
1987 return Fail("%s: Could not read output 0", __func__);
1988 }
1989
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001990 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001991 if (IsDynamicTensor(outputInfo))
1992 {
1993 return Fail("%s: Dynamic output tensors are not supported", __func__);
1994 }
1995
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001996 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01001997
1998 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1999
2000 if (desc.m_BlockSize <= 1)
2001 {
2002 return Fail("%s: Block size must be at least 1 in all dimensions");
2003 }
2004
2005 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
2006
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002007 bool isSupported = false;
2008 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2009 IsSpaceToDepthSupported,
2010 data.m_Backends,
2011 isSupported,
2012 inputInfo,
2013 outputInfo,
2014 desc);
2015 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01002016 {
2017 return false;
2018 }
2019
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002020 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002021 assert(layer != nullptr);
2022 input.Connect(layer->GetInputSlot(0));
2023
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002024 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002025}
2026
Francis Murtagh074c25a2019-07-22 16:40:57 +01002027bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
2028{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002029 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
2030
Francis Murtagh074c25a2019-07-22 16:40:57 +01002031 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2032 if (!input.IsValid())
2033 {
2034 return Fail("%s: Operation has invalid inputs", __func__);
2035 }
2036
2037 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2038 if (!outputOperand)
2039 {
2040 return Fail("%s: Operation has no outputs", __func__);
2041 }
2042
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002043 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01002044 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01002045 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002046 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002047 }
2048
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002049 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01002050 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
2051 {
2052 return Fail("%s: Operation has invalid inputs", __func__);
2053 }
2054
2055 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
2056 2,
2057 HalPolicy::OperandType::INT32,
2058 desc.m_Axis,
2059 model,
2060 data))
2061 {
2062 return Fail("%s: Operation has invalid inputs", __func__);
2063 }
2064
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01002065 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2066 !(desc.m_Axis == 1 ||
2067 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2068 {
2069 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2070 }
2071
Francis Murtagh074c25a2019-07-22 16:40:57 +01002072 bool isSupported = false;
2073 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2074 IsSoftmaxSupported,
2075 data.m_Backends,
2076 isSupported,
2077 input.GetTensorInfo(),
2078 outputInfo,
2079 desc);
2080 if (!isSupported)
2081 {
2082 return false;
2083 }
2084
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002085 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002086 assert(layer != nullptr);
2087 input.Connect(layer->GetInputSlot(0));
2088
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002089 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002090}
2091
Mike Kelly0a879362019-07-29 16:56:31 +01002092bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2093{
2094 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2095 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2096}
2097
Sadik Armagan61113162019-07-25 09:09:40 +01002098bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2099{
2100 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2101 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2102}
2103
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002104bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2105{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002106 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2107
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002108 // Inputs:
2109 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2110 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2111 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2112 if (!input.IsValid())
2113 {
2114 return Fail("%s: Could not read input 0: input", __func__);
2115 }
2116 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2117 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2118 if (!outputStateIn.IsValid())
2119 {
2120 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2121 }
2122 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2123 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2124 if (!cellStateIn.IsValid())
2125 {
2126 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2127 }
2128
2129 // Get the mandatory input tensors:
2130 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2131 // [num_units, input_size].
2132 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002133 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002134 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2135 // [num_units, input_size].
2136 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002137 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002138 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2139 // [num_units, input_size].
2140 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002141 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002142 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2143 // [num_units, output_size].
2144 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002145 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002146 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2147 // [num_units, output_size].
2148 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002149 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002150 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2151 // [num_units, output_size].
2152 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002153 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002154 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2155 const ConstTensorPin forgetGateBiasPin =
2156 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2157 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2158 const ConstTensorPin cellBiasPin =
2159 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2160 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2161 const ConstTensorPin outputGateBiasPin =
2162 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2163
2164 if (!inputToForgetWeightsPin.IsValid() ||
2165 !inputToCellWeightsPin.IsValid() ||
2166 !inputToOutputWeightsPin.IsValid() ||
2167 !recurrentToForgetWeightsPin.IsValid() ||
2168 !recurrentToCellWeightsPin.IsValid() ||
2169 !recurrentToOutputWeightsPin.IsValid() ||
2170 !forgetGateBiasPin.IsValid() ||
2171 !cellBiasPin.IsValid() ||
2172 !outputGateBiasPin.IsValid())
2173 {
2174 return Fail("%s: Operation has invalid tensor inputs", __func__);
2175 }
2176
2177 // Get the optional input tensors:
2178 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2179 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2180 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002181 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002182 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2183 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2184 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2185 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002186 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002187 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2188 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002189 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002190 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2191 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002192 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002193 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2194 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002195 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002196 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2197 const ConstTensorPin inputGateBiasPin =
2198 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2199 12,
2200 model,
2201 data,
2202 g_DontPermute,
2203 nullptr,
2204 true);
2205
2206 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2207 // [output_size, num_units].
2208 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002209 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002210 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2211 const ConstTensorPin projectionBiasPin =
2212 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2213 17,
2214 model,
2215 data,
2216 g_DontPermute,
2217 nullptr,
2218 true);
2219
2220 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2221 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2222 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2223 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2224 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2225 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2226 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2227 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2228 {
2229 return Fail("%s: Operation has invalid tensor inputs", __func__);
2230 }
2231
2232 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2233 // 20: The activation function: A value indicating the activation function:
2234 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2235 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2236 // If set to 0.0 then clipping is disabled.
2237 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2238 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2239 ActivationFn activation;
2240 float cellClip;
2241 float projClip;
2242 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2243 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2244 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2245 {
2246 return Fail("%s: Operation has invalid scalar inputs", __func__);
2247 }
2248
2249 // Get the normalization tensors
2250 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2251 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002252 const ConstTensorPin inputLayerNormWeightsPin
2253 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002254
2255 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2256 // Used to rescale normalized inputs to activation at forget gate.
2257 const ConstTensorPin forgetLayerNormWeightsPin =
2258 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2259 24,
2260 model,
2261 data,
2262 g_DontPermute,
2263 nullptr,
2264 true);
2265
2266 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2267 // Used to rescale normalized inputs to activation at cell gate.
2268 const ConstTensorPin cellLayerNormWeightsPin =
2269 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2270 25,
2271 model,
2272 data,
2273 g_DontPermute,
2274 nullptr,
2275 true);
2276
2277 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2278 // Used to rescale normalized inputs to activation at output gate.
2279 const ConstTensorPin outputLayerNormWeightsPin =
2280 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2281 26,
2282 model,
2283 data,
2284 g_DontPermute,
2285 nullptr,
2286 true);
2287
2288 // Outputs:
2289 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2290 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2291 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2292 if (!scratchBuffer)
2293 {
2294 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2295 }
2296 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2297 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2298 if (!outputStateOut)
2299 {
2300 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2301 }
2302 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2303 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2304 if (!cellStateOut)
2305 {
2306 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2307 }
2308 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2309 // effectively the same as the current “output state (out)” value.
2310 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2311 if (!output)
2312 {
2313 return Fail("%s: Could not read output 3: output", __func__);
2314 }
2315
2316 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002317 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002318 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2319 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2320 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2321 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2322 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2323 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2324 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2325 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2326 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2327 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2328 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2329 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2330 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2331 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2332 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2333 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2334 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2335 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2336 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2337 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2338 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2339
2340 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002341 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002342 desc.m_ActivationFunc = activation;
2343 desc.m_ClippingThresCell = cellClip;
2344 desc.m_ClippingThresProj = projClip;
2345 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2346 params.m_RecurrentToInputWeights == nullptr ||
2347 params.m_InputGateBias == nullptr);
2348 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2349 params.m_CellToOutputWeights != nullptr);
2350 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2351 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2352 params.m_ForgetLayerNormWeights != nullptr ||
2353 params.m_CellLayerNormWeights != nullptr ||
2354 params.m_OutputLayerNormWeights != nullptr);
2355
2356 // validate the optional input groups
2357 if (desc.m_CifgEnabled &&
2358 (params.m_InputToInputWeights != nullptr ||
2359 params.m_RecurrentToInputWeights != nullptr ||
2360 params.m_InputGateBias != nullptr))
2361 {
2362 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2363 " and input gate bias must be provided", __func__);
2364 }
2365
2366 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2367 {
2368 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2369 }
2370
2371 if (desc.m_PeepholeEnabled &&
2372 (params.m_CellToForgetWeights == nullptr ||
2373 params.m_CellToOutputWeights == nullptr ||
2374 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2375 {
2376 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2377 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2378 }
2379
2380 if (desc.m_LayerNormEnabled &&
2381 (params.m_ForgetLayerNormWeights == nullptr ||
2382 params.m_CellLayerNormWeights == nullptr ||
2383 params.m_OutputLayerNormWeights == nullptr ||
2384 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2385 {
2386 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2387 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2388 }
2389
2390 // Check if the layer is supported
2391 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002392 const TensorInfo& inputInfo = input.GetTensorInfo();
2393 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2394 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002395
2396 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002397 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2398 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2399 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2400 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002401
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002402 if (IsDynamicTensor(scratchBufferInfo) ||
2403 IsDynamicTensor(outputStateOutInfo) ||
2404 IsDynamicTensor(cellStateOutInfo) ||
2405 IsDynamicTensor(outputInfo))
2406 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002407 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2408 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2409 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002410 }
2411
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002412 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002413 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002414 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2415 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2416 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2417 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2418 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2419 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2420 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2421 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2422 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2423
2424 // Optional parameters
2425 if(!desc.m_CifgEnabled)
2426 {
2427 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2428 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2429 if (params.m_CellToInputWeights != nullptr)
2430 {
2431 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2432 }
2433 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2434 }
2435
2436 if(desc.m_ProjectionEnabled)
2437 {
2438 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2439 if (params.m_ProjectionBias != nullptr)
2440 {
2441 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2442 }
2443 }
2444
2445 if(desc.m_PeepholeEnabled)
2446 {
2447 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2448 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2449 }
2450
2451 if (desc.m_LayerNormEnabled)
2452 {
2453 if(!desc.m_CifgEnabled)
2454 {
2455 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2456 }
2457 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2458 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2459 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2460 }
2461
2462 bool isSupported = false;
2463 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2464 IsLstmSupported,
2465 data.m_Backends,
2466 isSupported,
2467 inputInfo,
2468 outputStateInInfo,
2469 cellStateInInfo,
2470 scratchBufferInfo,
2471 outputStateOutInfo,
2472 cellStateOutInfo,
2473 outputInfo,
2474 desc,
2475 paramsInfo);
2476 if (!isSupported)
2477 {
2478 return false;
2479 }
2480
2481 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002482 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002483
2484 input.Connect(layer->GetInputSlot(0));
2485 outputStateIn.Connect(layer->GetInputSlot(1));
2486 cellStateIn.Connect(layer->GetInputSlot(2));
2487
2488 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
2489 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2490 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2491 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2492}
2493
Sadik Armagan701d9a02019-09-04 15:16:18 +01002494bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2495{
2496 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002497 ActivationDescriptor desc;
2498 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002499
2500 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2501}
2502
Mike Kelly46272802019-08-14 17:00:48 +01002503bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2504{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002505 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002506 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2507}
2508
2509bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2510{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002511 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002512 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2513}
2514
2515bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2516{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002517 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002518 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2519}
2520
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002521bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002522{
2523 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2524
2525 if (!input.IsValid())
2526 {
2527 return Fail("%s: Operation has invalid inputs", __func__);
2528 }
2529
2530 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2531
2532 if (!output)
2533 {
2534 return Fail("%s: Could not read output 0", __func__);
2535 }
2536
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002537 const TensorInfo& inputInfo = input.GetTensorInfo();
2538 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002539 if (IsDynamicTensor(outputInfo))
2540 {
2541 return Fail("%s: Dynamic output tensors are not supported", __func__);
2542 }
2543
2544 // ArmNN does not currently support non-fixed weights or bias
2545 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2546 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2547
2548 if (weightsOperand == nullptr)
2549 {
2550 return Fail("%s: Operand is invalid", __func__);
2551 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002552 TransposeConvolution2dDescriptor desc;
2553 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002554
2555 // Determine whether padding is implicit or explicit
2556 bool implicitPadding = operation.inputs.size() == 9;
2557
2558 if (implicitPadding )
2559 {
2560 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2561 }
2562 else
2563 {
2564 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2565 }
2566
2567 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2568 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2569 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2570
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002571 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002572
2573 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2574 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002575 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002576 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2577 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2578
2579 // Bias is a 1D tensor
2580 const ConstTensorPin biasPin =
2581 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2582
2583 if (!weightsPin.IsValid())
2584 {
2585 return Fail("%s: Operation has invalid weights", __func__);
2586 }
2587
2588 if (!biasPin.IsValid())
2589 {
2590 return Fail("%s: Operation has invalid biases", __func__);
2591 }
2592
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002593 ConstTensor weights = weightsPin.GetConstTensor();
2594 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002595 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2596
2597 ActivationFn activation;
2598
2599 if (implicitPadding)
2600 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002601 int32_t strideX{0};
2602 int32_t strideY{0};
2603 int32_t padLeft{0};
2604 int32_t padRight{0};
2605 int32_t padTop{0};
2606 int32_t padBottom{0};
2607
David Monahan613b49c2019-06-27 11:37:47 +01002608 android::nn::PaddingScheme paddingScheme;
2609 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002610 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2611 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002612 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2613 {
2614 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2615 }
2616
2617 const uint32_t kernelX = weights.GetShape()[widthIndex];
2618 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002619 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2620 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002621
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002622 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2623 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2624
2625 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2626 // but Arm NN only supports values >= 0
2627 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2628 {
2629 return Fail("%s: Negative padding values are not supported", __func__);
2630 }
2631
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002632 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2633 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002634 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2635 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2636 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2637 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002638 }
2639 else if (operation.inputs.size() == 11)
2640 {
2641 // explicit padding
2642 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2643 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2644 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2645 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2646 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2647 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2648 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2649 {
2650 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2651 }
2652 }
2653 else
2654 {
2655 return Fail("%s: Unsupported number of operation inputs", __func__);
2656 }
2657
2658 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002659 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002660
2661 bool isSupported = false;
2662 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2663 IsTransposeConvolution2dSupported,
2664 data.m_Backends,
2665 isSupported,
2666 inputInfo,
2667 outputInfo,
2668 desc,
2669 weights.GetInfo(),
2670 biases);
2671 if (!isSupported)
2672 {
2673 return false;
2674 }
2675
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002676 IConnectableLayer* startLayer =
2677 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002678 if (!startLayer)
2679 {
2680 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2681 }
2682
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002683 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002684 if (!endLayer)
2685 {
2686 return Fail("%s: ProcessActivation failed", __func__);
2687 }
2688
2689 input.Connect(startLayer->GetInputSlot(0));
2690
2691 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2692}
2693
Mike Kellyb5fdf382019-06-11 16:35:25 +01002694} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002695} // namespace armnn_driver