blob: 2cb84972946eb0a8026763de088efe69b8878c59 [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 Murtagha23334e2019-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 Murtagha23334e2019-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(),
Kevin Maydbbcc392019-12-12 16:33:31 +0000705 outputInfo,
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100706 reshapeDescriptor);
707
708 if (!isSupported)
709 {
710 return false;
711 }
712
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100713 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100714 assert(layer != nullptr);
715 input.Connect(layer->GetInputSlot(0));
716
717 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
718}
719
Mike Kelly46272802019-08-14 17:00:48 +0100720bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
721{
722 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
723 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
724}
725
726bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
727{
728 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
729 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
730}
731
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100732bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
733{
734 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
735
736 //
737 // Parse data
738 //
739 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
740 if (!input.IsValid())
741 {
742 return Fail("%s: Operation has invalid inputs", __func__);
743 }
744 const TensorInfo& inputInfo = input.GetTensorInfo();
745
746 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
747 if (!output)
748 {
749 return Fail("%s: Could not read output 0", __func__);
750 }
751 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
752 if (IsDynamicTensor(outputInfo))
753 {
754 return Fail("%s: Dynamic output tensors are not supported", __func__);
755 }
756
757 // Look ahead to determine data layout
758 DataLayout dataLayout = DataLayout::NHWC;
759 if (operation.inputs.size() == 12)
760 {
761 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
762 }
763 else
764 {
765 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
766 }
767
768 // NOTE:
769 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
770 // but Arm NN expects the filter's height and width indices to match the input's height and
771 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
772 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
773 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
774 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
775 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
776 const ConstTensorPin biasesPin =
777 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
778 if (!weightsPin.IsValid() || !biasesPin.IsValid())
779 {
780 return Fail("%s: Operation has invalid inputs", __func__);
781 }
782
783 ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000784 ConstTensor biases = biasesPin.GetConstTensor();
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100785 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
786
787 const TensorShape& inputShape = inputInfo.GetShape();
788 const TensorShape& outputShape = outputInfo.GetShape();
789 const TensorShape& weightsShape = weights.GetShape();
790 const TensorShape& biasesShape = biases.GetShape();
791
792 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
793 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
794 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
795 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
796
797 Convolution2dDescriptor desc;
798 desc.m_DataLayout = dataLayout;
799 desc.m_BiasEnabled = true;
800
801 int numGroups;
802 ActivationFn activation;
803
804 if (operation.inputs.size() == 12)
805 {
806 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
807 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
808 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
809 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
810 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
811 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
812 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
813 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
814 {
815 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
816 }
817
818 }
819 else if (operation.inputs.size() == 9)
820 {
821 android::nn::PaddingScheme paddingScheme;
822 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
823 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
824 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
825 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
826 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
827 {
828 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
829 }
830
831 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
832 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
833
834 const uint32_t kernelX = weightsShape[widthIndex];
835 const uint32_t kernelY = weightsShape[heightIndex];
836
837 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
838 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
839 }
840 else
841 {
842 return Fail("%s: Unsupported number of operation inputs", __func__);
843 }
844
845 const unsigned int outputChannels = outputShape[channelsIndex];
846
847 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
848 const unsigned int channelMultiplier = outputChannels / numGroups;
849
850 //
851 // Validate all relevant inputs
852 //
853 if (numGroups <= 0)
854 {
855 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
856 }
857
858 if (outputChannels % numGroups != 0u)
859 {
860 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
861 }
862
863 //
864 // Set up Splitter layer
865 //
866 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
867 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
868
869 TensorInfo splitterOutputInfo(4,
870 splitterDimSizes,
871 inputInfo.GetDataType(),
872 inputInfo.GetQuantizationScale(),
873 inputInfo.GetQuantizationOffset());
874
875 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
876
877 ViewsDescriptor splitterDesc(numGroups);
878 for (unsigned int group = 0u; group < numGroups; ++group)
879 {
880 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
881 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
882 {
883 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
884 }
885 }
886
887 bool isSupported = false;
888 FORWARD_LAYER_SUPPORT_FUNC(__func__,
889 IsSplitterSupported,
890 data.m_Backends,
891 isSupported,
892 inputInfo,
893 splitterOutputInfos,
894 splitterDesc);
895 if (!isSupported)
896 {
897 return false;
898 }
899
900 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
901 if (!splitterLayer)
902 {
903 return Fail("%s: Failed to add SplitterLayer", __func__);
904 }
905
906 input.Connect(splitterLayer->GetInputSlot(0));
907 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
908 {
909 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
910 }
911
912 //
913 // Set up Convolution2d layers for each group
914 //
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000915
916 // Set up group tensor shapes
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100917 TensorShape groupInputShape(inputShape);
918 groupInputShape[channelsIndex] = channelsPerGroup;
919
920 TensorShape groupOutputShape(outputShape);
921 groupOutputShape[channelsIndex] = 1;
922
923 TensorShape groupWeightsShape(weightsShape);
924 groupWeightsShape[0] /= channelMultiplier * numGroups;
925
926 TensorShape groupBiasesShape({ 1 });
927
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000928 // Set up group tensor infos
929 TensorInfo groupInputInfo(inputInfo);
930 groupInputInfo.SetShape(groupInputShape);
931
932 const TensorInfo& weightsInfo = weights.GetInfo();
933 TensorInfo groupWeightsInfo(weightsInfo);
934 groupWeightsInfo.SetShape(groupWeightsShape);
935
936 const TensorInfo& biasesInfo = biases.GetInfo();
937 TensorInfo groupBiasesInfo(biasesInfo);
938 groupBiasesInfo.SetShape(groupBiasesShape);
939
940 TensorInfo groupOutputInfo(outputInfo);
941 groupOutputInfo.SetShape(groupOutputShape);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100942
943 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
944 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
945
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000946 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100947 for (unsigned int group = 0u; group < numGroups; ++group)
948 {
949 for (unsigned int m = 0u; m < channelMultiplier; ++m)
950 {
951 auto index = group * channelMultiplier + m;
952
953 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
954 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
955
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000956 if (weightsInfo.HasPerAxisQuantization())
957 {
958 // Extract per-axis quantization scales for group weights
959 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
960 groupWeightsInfo.SetQuantizationScales(
961 std::vector<float>(weightsQuantScales.begin() + index,
962 weightsQuantScales.begin() + index + groupWeightsShape[0]));
963
964 // Extract per-axis quantization scales for group biases
965 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
966 groupBiasesInfo.SetQuantizationScales(
967 std::vector<float>(biasesQuantScales.begin() + index,
968 biasesQuantScales.begin() + index + groupWeightsShape[0]));
969 }
970
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100971 // Extract weights and biases data for current group convolution
972 ConstTensor groupWeights(groupWeightsInfo,
973 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
974 weightsDataOffset));
975 ConstTensor groupBiases(groupBiasesInfo,
976 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
977 biasesDataOffset));
978
979 isSupported = false;
980 FORWARD_LAYER_SUPPORT_FUNC(__func__,
981 IsConvolution2dSupported,
982 data.m_Backends,
983 isSupported,
984 groupInputInfo,
985 groupOutputInfo,
986 desc,
987 groupWeightsInfo,
988 Optional<TensorInfo>(groupBiasesInfo));
989 if (!isSupported)
990 {
991 return false;
992 }
993
994 IConnectableLayer *convLayer =
995 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
996 if (!convLayer)
997 {
998 return Fail("%s: AddConvolution2dLayer failed", __func__);
999 }
1000
1001 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1002 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1003
1004 convLayers[index] = convLayer;
1005 }
1006 }
1007
1008 //
1009 // Set up Concat layer
1010 //
1011 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
1012 for (unsigned int group = 0u; group < numGroups; ++group)
1013 {
1014 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1015 {
1016 auto index = group * channelMultiplier + m;
1017 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1018 concatDescriptor.SetConcatAxis(channelsIndex);
1019 }
1020 }
1021
1022 isSupported = false;
1023 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1024 IsConcatSupported,
1025 data.m_Backends,
1026 isSupported,
1027 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1028 outputInfo,
1029 concatDescriptor);
1030 if (!isSupported)
1031 {
1032 return false;
1033 }
1034
1035 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1036 if (!concatLayer)
1037 {
1038 return Fail("%s: AddConcatLayer failed", __func__);
1039 }
1040
1041 for (unsigned int group = 0u; group < numGroups; ++group)
1042 {
1043 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1044 {
1045 auto index = group * channelMultiplier + m;
1046 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1047 }
1048 }
1049 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1050
1051 //
1052 // Set up Activation layer (if it is set)
1053 //
1054 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
1055 if (!endLayer)
1056 {
1057 return Fail("%s: ProcessActivation failed", __func__);
1058 }
1059
1060 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1061}
1062
Aron Virginas-Tara2a73802019-10-09 15:30:40 +01001063bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
1064{
1065 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
1066
1067 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1068 if (!input.IsValid())
1069 {
1070 return Fail("%s: Operation has an invalid input 0", __func__);
1071 }
1072
1073 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1074 if (!output)
1075 {
1076 return Fail("%s: Operation has an invalid output", __func__);
1077 }
1078
1079 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1080 if (IsDynamicTensor(outputInfo))
1081 {
1082 return Fail("%s: Dynamic output tensors are not supported", __func__);
1083 }
1084
1085 // Determine data type of input tensor
1086 OperandType inputType;
1087 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1088 {
1089 return Fail("%s: Operation has invalid inputs", __func__);
1090 }
1091
1092 InstanceNormalizationDescriptor desc;
1093
1094 // Read gamma, beta & epsilon
1095 if (inputType == OperandType::TENSOR_FLOAT16)
1096 {
1097 Half fp16Gamma;
1098 Half fp16Beta;
1099 Half fp16Epsilon;
1100
1101 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1102 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1103 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1104 {
1105 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1106 }
1107
1108 desc.m_Gamma = static_cast<float>(fp16Gamma);
1109 desc.m_Beta = static_cast<float>(fp16Beta);
1110 desc.m_Eps = static_cast<float>(fp16Epsilon);
1111 }
1112 else if (inputType == OperandType::TENSOR_FLOAT32)
1113 {
1114 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1115 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1116 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1117 {
1118 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1119 }
1120 }
1121 else
1122 {
1123 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1124 }
1125
1126 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1127
1128 bool isSupported = false;
1129 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1130 IsInstanceNormalizationSupported,
1131 data.m_Backends,
1132 isSupported,
1133 input.GetTensorInfo(),
1134 outputInfo,
1135 desc);
1136 if (!isSupported)
1137 {
1138 return false;
1139 }
1140
1141 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1142 input.Connect(layer->GetInputSlot(0));
1143
1144 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1145}
1146
Mike Kelly46272802019-08-14 17:00:48 +01001147bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1148{
1149 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1150 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1151}
1152
Sadik Armagan15d63e22019-07-26 16:59:35 +01001153bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1154{
1155 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001156 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001157}
1158
Mike Kelly46272802019-08-14 17:00:48 +01001159bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1160 const Model& model,
1161 ConversionData& data)
1162{
1163 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1164 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1165}
1166
1167bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1168{
1169 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1170 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1171}
1172
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001173bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1174{
1175 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1176
1177 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1178 if (!input.IsValid())
1179 {
1180 return Fail("%s: Failed to read input 0", __func__);
1181 }
1182
1183 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1184 if (!output)
1185 {
1186 return Fail("%s: Failed to read output", __func__);
1187 }
1188
1189 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1190 if (IsDynamicTensor(outputInfo))
1191 {
1192 return Fail("%s: Dynamic output tensors are not supported", __func__);
1193 }
1194
1195 // Determine data type of input tensor
1196 OperandType inputType;
1197 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1198 {
1199 return Fail("%s: Operation has invalid inputs", __func__);
1200 }
1201
1202 LogSoftmaxDescriptor descriptor;
1203
1204 // Read beta
1205 if (inputType == OperandType::TENSOR_FLOAT16)
1206 {
1207 Half fp16Beta;
1208 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1209 {
1210 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1211 }
1212
1213 descriptor.m_Beta = static_cast<float>(fp16Beta);
1214 }
1215 else if (inputType == OperandType::TENSOR_FLOAT32)
1216 {
1217 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1218 {
1219 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1220 }
1221 }
1222 else
1223 {
1224 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1225 }
1226
1227 // Read axis
1228 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1229 {
1230 return Fail("%s: Failed to read input 2", __func__);
1231 }
1232
1233 bool isSupported = false;
1234 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1235 IsLogSoftmaxSupported,
1236 data.m_Backends,
1237 isSupported,
1238 input.GetTensorInfo(),
1239 outputInfo,
1240 descriptor);
1241 if (!isSupported)
1242 {
1243 return false;
1244 }
1245
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001246 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001247 if (!layer)
1248 {
1249 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1250 }
1251
1252 input.Connect(layer->GetInputSlot(0));
1253
1254 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1255}
1256
Sadik Armagan15d63e22019-07-26 16:59:35 +01001257bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1258{
1259 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001260 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001261}
1262
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001263bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1264{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001265 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1266
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001267 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1268 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1269
1270 if (!input0.IsValid() || !input1.IsValid())
1271 {
1272 return Fail("%s: Operation has invalid inputs", __func__);
1273 }
1274
1275 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1276 if (!outputOperand)
1277 {
1278 return Fail("%s: Could not read output", __func__);
1279 }
1280
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001281 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001282 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001283 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001284 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001285 }
1286
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001287 bool isSupported = false;
1288 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1289 IsMaximumSupported,
1290 data.m_Backends,
1291 isSupported,
1292 input0.GetTensorInfo(),
1293 input1.GetTensorInfo(),
1294 outInfo);
1295
1296 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001297 {
1298 return false;
1299 }
1300
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001301 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001302 assert(layer != nullptr);
Kevin Maydbbcc392019-12-12 16:33:31 +00001303 bool isReshapeSupported = BroadcastTensor(input0, input1, outInfo, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001304 if (!isReshapeSupported)
1305 {
1306 return false;
1307 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001308
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001309 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001310}
1311
Mike Kelly46272802019-08-14 17:00:48 +01001312bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1313{
1314 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1315 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1316}
1317
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001318bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1319{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001320 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1321
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001322 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1323 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1324
1325 if (!input0.IsValid() || !input1.IsValid())
1326 {
1327 return Fail("%s: Operation has invalid inputs", __func__);
1328 }
1329
1330 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1331 if (!output)
1332 {
1333 return Fail("%s: Could not read output 0", __func__);
1334 }
1335
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001336 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001337 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001338 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001339 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001340 }
1341
1342 bool isSupported = false;
1343 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1344 IsMinimumSupported,
1345 data.m_Backends,
1346 isSupported,
1347 input0.GetTensorInfo(),
1348 input1.GetTensorInfo(),
1349 outputInfo);
1350
1351 if (!isSupported)
1352 {
1353 return false;
1354 }
1355
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001356 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001357 assert(layer != nullptr);
Kevin Maydbbcc392019-12-12 16:33:31 +00001358 bool isReshapeSupported = BroadcastTensor(input0, input1, outputInfo, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001359 if (!isReshapeSupported)
1360 {
1361 return false;
1362 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001363
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001364 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001365}
1366
Mike Kelly46272802019-08-14 17:00:48 +01001367bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1368{
1369 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1370 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1371}
1372
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001373bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1374{
1375 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1376 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1377}
1378
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001379bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1380{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001381 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1382
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001383 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1384 if (!input.IsValid())
1385 {
1386 return Fail("%s: Could not read input 0", __func__);
1387 }
1388
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001389 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1390 if (!output)
1391 {
1392 return Fail("%s: Could not read output", __func__);
1393 }
1394
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001395 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001396 unsigned int rank = inputInfo.GetNumDimensions();
1397
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001398 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001399 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1400 {
1401 return Fail("%s: Could not convert paddings", __func__);
1402 }
1403
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001404 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001405 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001406 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001407 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001408 }
1409
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001410 // Determine type of padding value
1411 OperandType operandType0;
1412 OperandType operandType2;
1413
1414 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1415 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1416 {
1417 return Fail("%s: Operation has invalid inputs", __func__);
1418 }
1419
1420 // Read value to use for padding
1421 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1422 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001423 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001424 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1425 {
1426 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1427 }
1428
1429 descriptor.m_PadValue = f16PadValue;
1430 }
1431 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1432 {
1433 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1434 {
1435 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1436 }
1437 }
1438 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1439 {
Mike Kelly3c673942019-07-25 09:26:06 +01001440 int32_t intPadValue = 0;
1441 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001442 {
1443 return Fail("%s: Could not read input 2 (INT32)", __func__);
1444 }
Mike Kelly3c673942019-07-25 09:26:06 +01001445 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001446 }
1447 else
1448 {
1449 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1450 }
1451
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001452 bool isSupported = false;
1453 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1454 IsPadSupported,
1455 data.m_Backends,
1456 isSupported,
1457 inputInfo,
1458 outputInfo,
1459 descriptor);
1460 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001461 {
1462 return false;
1463 }
1464
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001465 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001466 assert(layer != nullptr);
1467 input.Connect(layer->GetInputSlot(0));
1468 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1469
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001470 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001471}
1472
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001473bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1474{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001475 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1476
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001477 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1478 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1479
1480 if (!input.IsValid() || !alpha.IsValid())
1481 {
1482 return Fail("%s: Operation has invalid inputs", __func__);
1483 }
1484
1485 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1486
1487 if (!output)
1488 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001489 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001490 }
1491
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001492 const TensorInfo& inputInfo = input.GetTensorInfo();
1493 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1494 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001495
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001496 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001497 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001498 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001499 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001500
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001501 bool isSupported = false;
1502 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1503 IsPreluSupported,
1504 data.m_Backends,
1505 isSupported,
1506 inputInfo,
1507 alphaInfo,
1508 outputInfo);
1509 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001510 {
1511 return false;
1512 }
1513
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001514 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001515
1516 if (!layer)
1517 {
1518 return Fail("%s: AddPreluLayer failed", __func__);
1519 }
1520
Kevin Maydbbcc392019-12-12 16:33:31 +00001521 bool isReshapeSupported = BroadcastTensor(input, alpha, outputInfo, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001522 if (!isReshapeSupported)
1523 {
1524 return false;
1525 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001526
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001527 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001528}
1529
Sadik Armagan5a476a82019-07-30 09:43:18 +01001530bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1531{
1532 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1533
1534 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1535 if (!input.IsValid())
1536 {
1537 return Fail("%s: Operation has invalid input", __func__);
1538 }
1539
1540 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1541 if (!outputOperand)
1542 {
1543 return Fail("%s: Operation has invalid outputs", __func__);
1544 }
1545
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001546 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001547 if (IsDynamicTensor(outputInfo))
1548 {
1549 return Fail("%s: Dynamic output tensors are not supported", __func__);
1550 }
1551
1552 bool isSupported = false;
1553 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1554 IsQuantizeSupported,
1555 data.m_Backends,
1556 isSupported,
1557 input.GetTensorInfo(),
1558 outputInfo);
1559 if (!isSupported)
1560 {
1561 return false;
1562 }
1563
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001564 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001565 assert(layer != nullptr);
1566 input.Connect(layer->GetInputSlot(0));
1567
1568 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1569}
1570
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001571bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1572{
1573 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1574
1575 //Inputs:
1576 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1577 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1578 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1579 if (!input.IsValid())
1580 {
1581 return Fail("%s: Could not read input 0: input", __func__);
1582 }
1583
1584 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1585 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1586 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1587 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1588 if (!previousCellStateIn.IsValid())
1589 {
1590 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1591 }
1592
1593 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1594 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1595 // is quantized with a fixed quantization range of -1, 127/128.
1596 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1597 if (!previousOutputIn.IsValid())
1598 {
1599 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1600 }
1601
1602 // Get the input tensors:
1603 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1604 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1605 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1606 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001607 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001608
1609 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1610 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1611 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1612 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001613 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001614
1615 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1616 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1617 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1618 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001619 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001620
1621 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1622 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1623 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1624 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001625 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001626
1627 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1628 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1629 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1630 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001631 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001632
1633 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1634 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1635 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1636 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001637 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001638
1639 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1640 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1641 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1642 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001643 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001644
1645 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1646 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1647 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1648 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001649 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001650
1651 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1652 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1653 // of input and weights scales and zeroPoint equal to 0.
1654 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001655 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001656
1657 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1658 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1659 // of input and weights scales and zeroPoint equal to 0.
1660 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001661 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001662
1663 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1664 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1665 // and weights scales and zeroPoint equal to 0.
1666 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001667 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001668
1669 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1670 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1671 // of input and weights scales and zeroPoint equal to 0.
1672 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001673 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001674
1675 if (!inputToInputWeightsPin.IsValid() ||
1676 !inputToForgetWeightsPin.IsValid() ||
1677 !inputToCellWeightsPin.IsValid() ||
1678 !inputToOutputWeightsPin.IsValid() ||
1679 !recurrentToInputWeightsPin.IsValid() ||
1680 !recurrentToForgetWeightsPin.IsValid() ||
1681 !recurrentToCellWeightsPin.IsValid() ||
1682 !recurrentToOutputWeightsPin.IsValid() ||
1683 !inputGateBiasPin.IsValid() ||
1684 !forgetGateBiasPin.IsValid() ||
1685 !cellBiasPin.IsValid() ||
1686 !outputGateBiasPin.IsValid())
1687 {
1688 return Fail("%s: Operation has invalid tensor inputs", __func__);
1689 }
1690
1691 // Outputs:
1692 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1693 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1694 // of -2^4, 2^4 * 32767/32768.
1695 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1696 if (!cellStateOut)
1697 {
1698 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1699 }
1700
1701 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1702 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1703 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1704 if (!output)
1705 {
1706 return Fail("%s: Could not read output 1: output", __func__);
1707 }
1708
1709 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001710 const TensorInfo& inputInfo = input.GetTensorInfo();
1711 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1712 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001713
1714 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001715 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1716 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001717
1718 // Dynamic tensors currently not supported
1719 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1720 {
1721 return Fail("%s: Dynamic output tensors are not supported", __func__);
1722 }
1723
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001724 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001725
1726 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1727 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1728 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1729 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1730 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1731 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1732 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1733 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1734 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1735 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1736 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1737 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1738
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001739 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001740 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1741 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1742 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1743 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1744 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1745 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1746 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1747 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1748 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1749 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1750 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1751 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1752
1753 bool isSupported = false;
1754 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1755 IsQuantizedLstmSupported,
1756 data.m_Backends,
1757 isSupported,
1758 inputInfo,
1759 previousCellStateInInfo,
1760 previousOutputInInfo,
1761 cellStateOutInfo,
1762 outputInfo,
1763 paramsInfo);
1764
1765 if (!isSupported)
1766 {
1767 return false;
1768 }
1769
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001770 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001771 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001772 previousCellStateIn.Connect(layer->GetInputSlot(1));
1773 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001774
1775 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1776 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1777}
1778
Sadik Armagan61113162019-07-25 09:09:40 +01001779bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1780{
1781 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1782 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1783}
1784
1785bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1786{
1787 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1788 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1789}
1790
1791bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1792{
1793 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1794 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1795}
1796
Mike Kelly46272802019-08-14 17:00:48 +01001797bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1798{
1799 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1800 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1801}
1802
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001803bool HalPolicy::ConvertResize(const Operation& operation,
1804 const Model& model,
1805 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001806 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001807{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001808 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001809 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001810
1811 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001812 if (!input.IsValid())
1813 {
1814 return Fail("%s: Could not read input 0", __func__);
1815 }
1816
1817 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1818 if (!output)
1819 {
1820 return Fail("%s: Could not read output 0", __func__);
1821 }
1822
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001823 const TensorInfo& inputInfo = input.GetTensorInfo();
1824 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001825
1826 if (IsDynamicTensor(outputInfo))
1827 {
1828 return Fail("%s: Dynamic output tensors are not supported", __func__);
1829 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001830
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001831 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001832 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001833 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1834
1835 OperandType operandType1;
1836 OperandType operandType2;
1837
1838 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1839 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1840 {
1841 return Fail("%s: Operation has invalid inputs", __func__);
1842 }
1843
1844 if (operandType1 != operandType2)
1845 {
1846 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1847 }
1848
1849 if (operandType1 == OperandType::INT32)
1850 {
1851 // Case 1: resizing by shape
1852 int32_t targetWidth = 0;
1853 int32_t targetHeight = 0;
1854
1855 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1856 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1857 {
1858 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1859 }
1860
1861 if (targetWidth < 0 || targetHeight < 0)
1862 {
1863 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1864 "Target width/height cannot be < 0", __func__);
1865 }
1866
1867 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001868 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001869 }
1870 else if (operandType1 == OperandType::FLOAT32)
1871 {
1872 // Case 2: resizing by scale
1873 float widthScale = 1.0f;
1874 float heightScale = 1.0f;
1875
1876 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1877 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1878 {
1879 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1880 }
1881
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001882 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001883 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1884
1885 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1886 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1887
1888 descriptor.m_TargetWidth = std::floor(width * widthScale);
1889 descriptor.m_TargetHeight = std::floor(height * heightScale);
1890 }
1891 else
1892 {
1893 // NOTE: FLOAT16 scales are not supported
1894 return false;
1895 }
1896
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001897 bool isSupported = false;
1898 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1899 IsResizeSupported,
1900 data.m_Backends,
1901 isSupported,
1902 inputInfo,
1903 outputInfo,
1904 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001905
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001906 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001907 {
1908 return false;
1909 }
1910
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001911 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001912
1913 assert(layer != nullptr);
1914
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001915 input.Connect(layer->GetInputSlot(0));
1916
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001917 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001918}
1919
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001920bool HalPolicy::ConvertRsqrt(const Operation& operation, const Model& model, ConversionData& data)
1921{
1922 ALOGV("hal_1_2::HalPolicy::ConvertRsqrt()");
1923
1924 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1925 if (!input.IsValid())
1926 {
1927 return Fail("%s: Operation has invalid input", __func__);
1928 }
1929
1930 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1931 if (!output)
1932 {
1933 return Fail("%s: Could not read output 0", __func__);
1934 }
1935
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001936 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001937 if (IsDynamicTensor(outputInfo))
1938 {
1939 return Fail("%s: Dynamic output tensors are not supported", __func__);
1940 }
1941
1942 bool isSupported = false;
1943 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1944 IsRsqrtSupported,
1945 data.m_Backends,
1946 isSupported,
1947 input.GetTensorInfo(),
1948 outputInfo);
1949
1950 if (!isSupported)
1951 {
1952 return false;
1953 }
1954
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001955 IConnectableLayer* const layer = data.m_Network->AddRsqrtLayer();
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001956 assert(layer != nullptr);
1957 input.Connect(layer->GetInputSlot(0));
1958
1959 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1960}
1961
Finn Williamsd74c5052019-07-30 17:06:00 +01001962bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1963{
1964 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1965 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1966}
1967
Keith Davisa6bc52f2019-06-26 09:39:49 +01001968bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1969{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001970 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001971
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001972 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001973 if (!input.IsValid() )
1974 {
1975 return Fail("%s: Operation has invalid inputs", __func__);
1976 }
1977
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001978 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001979 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001980 if (rank != 4)
1981 {
1982 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1983 }
1984
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001985 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1986 if (!output)
1987 {
1988 return Fail("%s: Could not read output 0", __func__);
1989 }
1990
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001991 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001992 if (IsDynamicTensor(outputInfo))
1993 {
1994 return Fail("%s: Dynamic output tensors are not supported", __func__);
1995 }
1996
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001997 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01001998
1999 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
2000
2001 if (desc.m_BlockSize <= 1)
2002 {
2003 return Fail("%s: Block size must be at least 1 in all dimensions");
2004 }
2005
2006 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
2007
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002008 bool isSupported = false;
2009 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2010 IsSpaceToDepthSupported,
2011 data.m_Backends,
2012 isSupported,
2013 inputInfo,
2014 outputInfo,
2015 desc);
2016 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01002017 {
2018 return false;
2019 }
2020
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002021 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002022 assert(layer != nullptr);
2023 input.Connect(layer->GetInputSlot(0));
2024
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002025 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002026}
2027
Francis Murtagh074c25a2019-07-22 16:40:57 +01002028bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
2029{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002030 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
2031
Francis Murtagh074c25a2019-07-22 16:40:57 +01002032 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2033 if (!input.IsValid())
2034 {
2035 return Fail("%s: Operation has invalid inputs", __func__);
2036 }
2037
2038 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2039 if (!outputOperand)
2040 {
2041 return Fail("%s: Operation has no outputs", __func__);
2042 }
2043
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002044 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01002045 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01002046 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002047 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002048 }
2049
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002050 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01002051 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
2052 {
2053 return Fail("%s: Operation has invalid inputs", __func__);
2054 }
2055
2056 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
2057 2,
2058 HalPolicy::OperandType::INT32,
2059 desc.m_Axis,
2060 model,
2061 data))
2062 {
2063 return Fail("%s: Operation has invalid inputs", __func__);
2064 }
2065
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01002066 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2067 !(desc.m_Axis == 1 ||
2068 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2069 {
2070 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2071 }
2072
Francis Murtagh074c25a2019-07-22 16:40:57 +01002073 bool isSupported = false;
2074 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2075 IsSoftmaxSupported,
2076 data.m_Backends,
2077 isSupported,
2078 input.GetTensorInfo(),
2079 outputInfo,
2080 desc);
2081 if (!isSupported)
2082 {
2083 return false;
2084 }
2085
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002086 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002087 assert(layer != nullptr);
2088 input.Connect(layer->GetInputSlot(0));
2089
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002090 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002091}
2092
Mike Kelly0a879362019-07-29 16:56:31 +01002093bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2094{
2095 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2096 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2097}
2098
Sadik Armagan61113162019-07-25 09:09:40 +01002099bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2100{
2101 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2102 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2103}
2104
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002105bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2106{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002107 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2108
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002109 // Inputs:
2110 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2111 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2112 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2113 if (!input.IsValid())
2114 {
2115 return Fail("%s: Could not read input 0: input", __func__);
2116 }
2117 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2118 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2119 if (!outputStateIn.IsValid())
2120 {
2121 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2122 }
2123 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2124 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2125 if (!cellStateIn.IsValid())
2126 {
2127 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2128 }
2129
2130 // Get the mandatory input tensors:
2131 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2132 // [num_units, input_size].
2133 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002134 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002135 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2136 // [num_units, input_size].
2137 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002138 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002139 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2140 // [num_units, input_size].
2141 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002142 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002143 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2144 // [num_units, output_size].
2145 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002146 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002147 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2148 // [num_units, output_size].
2149 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002150 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002151 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2152 // [num_units, output_size].
2153 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002154 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002155 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2156 const ConstTensorPin forgetGateBiasPin =
2157 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2158 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2159 const ConstTensorPin cellBiasPin =
2160 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2161 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2162 const ConstTensorPin outputGateBiasPin =
2163 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2164
2165 if (!inputToForgetWeightsPin.IsValid() ||
2166 !inputToCellWeightsPin.IsValid() ||
2167 !inputToOutputWeightsPin.IsValid() ||
2168 !recurrentToForgetWeightsPin.IsValid() ||
2169 !recurrentToCellWeightsPin.IsValid() ||
2170 !recurrentToOutputWeightsPin.IsValid() ||
2171 !forgetGateBiasPin.IsValid() ||
2172 !cellBiasPin.IsValid() ||
2173 !outputGateBiasPin.IsValid())
2174 {
2175 return Fail("%s: Operation has invalid tensor inputs", __func__);
2176 }
2177
2178 // Get the optional input tensors:
2179 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2180 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2181 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002182 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002183 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2184 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2185 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2186 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002187 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002188 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2189 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002190 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002191 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2192 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002193 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002194 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2195 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002196 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002197 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2198 const ConstTensorPin inputGateBiasPin =
2199 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2200 12,
2201 model,
2202 data,
2203 g_DontPermute,
2204 nullptr,
2205 true);
2206
2207 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2208 // [output_size, num_units].
2209 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002210 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002211 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2212 const ConstTensorPin projectionBiasPin =
2213 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2214 17,
2215 model,
2216 data,
2217 g_DontPermute,
2218 nullptr,
2219 true);
2220
2221 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2222 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2223 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2224 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2225 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2226 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2227 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2228 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2229 {
2230 return Fail("%s: Operation has invalid tensor inputs", __func__);
2231 }
2232
2233 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2234 // 20: The activation function: A value indicating the activation function:
2235 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2236 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2237 // If set to 0.0 then clipping is disabled.
2238 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2239 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2240 ActivationFn activation;
2241 float cellClip;
2242 float projClip;
2243 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2244 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2245 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2246 {
2247 return Fail("%s: Operation has invalid scalar inputs", __func__);
2248 }
2249
2250 // Get the normalization tensors
2251 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2252 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002253 const ConstTensorPin inputLayerNormWeightsPin
2254 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002255
2256 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2257 // Used to rescale normalized inputs to activation at forget gate.
2258 const ConstTensorPin forgetLayerNormWeightsPin =
2259 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2260 24,
2261 model,
2262 data,
2263 g_DontPermute,
2264 nullptr,
2265 true);
2266
2267 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2268 // Used to rescale normalized inputs to activation at cell gate.
2269 const ConstTensorPin cellLayerNormWeightsPin =
2270 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2271 25,
2272 model,
2273 data,
2274 g_DontPermute,
2275 nullptr,
2276 true);
2277
2278 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2279 // Used to rescale normalized inputs to activation at output gate.
2280 const ConstTensorPin outputLayerNormWeightsPin =
2281 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2282 26,
2283 model,
2284 data,
2285 g_DontPermute,
2286 nullptr,
2287 true);
2288
2289 // Outputs:
2290 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2291 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2292 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2293 if (!scratchBuffer)
2294 {
2295 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2296 }
2297 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2298 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2299 if (!outputStateOut)
2300 {
2301 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2302 }
2303 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2304 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2305 if (!cellStateOut)
2306 {
2307 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2308 }
2309 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2310 // effectively the same as the current “output state (out)” value.
2311 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2312 if (!output)
2313 {
2314 return Fail("%s: Could not read output 3: output", __func__);
2315 }
2316
2317 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002318 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002319 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2320 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2321 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2322 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2323 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2324 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2325 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2326 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2327 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2328 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2329 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2330 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2331 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2332 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2333 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2334 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2335 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2336 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2337 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2338 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2339 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2340
2341 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002342 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002343 desc.m_ActivationFunc = activation;
2344 desc.m_ClippingThresCell = cellClip;
2345 desc.m_ClippingThresProj = projClip;
2346 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2347 params.m_RecurrentToInputWeights == nullptr ||
2348 params.m_InputGateBias == nullptr);
2349 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2350 params.m_CellToOutputWeights != nullptr);
2351 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2352 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2353 params.m_ForgetLayerNormWeights != nullptr ||
2354 params.m_CellLayerNormWeights != nullptr ||
2355 params.m_OutputLayerNormWeights != nullptr);
2356
2357 // validate the optional input groups
2358 if (desc.m_CifgEnabled &&
2359 (params.m_InputToInputWeights != nullptr ||
2360 params.m_RecurrentToInputWeights != nullptr ||
2361 params.m_InputGateBias != nullptr))
2362 {
2363 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2364 " and input gate bias must be provided", __func__);
2365 }
2366
2367 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2368 {
2369 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2370 }
2371
2372 if (desc.m_PeepholeEnabled &&
2373 (params.m_CellToForgetWeights == nullptr ||
2374 params.m_CellToOutputWeights == nullptr ||
2375 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2376 {
2377 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2378 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2379 }
2380
2381 if (desc.m_LayerNormEnabled &&
2382 (params.m_ForgetLayerNormWeights == nullptr ||
2383 params.m_CellLayerNormWeights == nullptr ||
2384 params.m_OutputLayerNormWeights == nullptr ||
2385 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2386 {
2387 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2388 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2389 }
2390
2391 // Check if the layer is supported
2392 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002393 const TensorInfo& inputInfo = input.GetTensorInfo();
2394 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2395 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002396
2397 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002398 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2399 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2400 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2401 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002402
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002403 if (IsDynamicTensor(scratchBufferInfo) ||
2404 IsDynamicTensor(outputStateOutInfo) ||
2405 IsDynamicTensor(cellStateOutInfo) ||
2406 IsDynamicTensor(outputInfo))
2407 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002408 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2409 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2410 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002411 }
2412
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002413 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002414 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002415 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2416 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2417 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2418 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2419 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2420 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2421 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2422 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2423 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2424
2425 // Optional parameters
2426 if(!desc.m_CifgEnabled)
2427 {
2428 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2429 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2430 if (params.m_CellToInputWeights != nullptr)
2431 {
2432 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2433 }
2434 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2435 }
2436
2437 if(desc.m_ProjectionEnabled)
2438 {
2439 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2440 if (params.m_ProjectionBias != nullptr)
2441 {
2442 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2443 }
2444 }
2445
2446 if(desc.m_PeepholeEnabled)
2447 {
2448 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2449 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2450 }
2451
2452 if (desc.m_LayerNormEnabled)
2453 {
2454 if(!desc.m_CifgEnabled)
2455 {
2456 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2457 }
2458 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2459 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2460 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2461 }
2462
2463 bool isSupported = false;
2464 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2465 IsLstmSupported,
2466 data.m_Backends,
2467 isSupported,
2468 inputInfo,
2469 outputStateInInfo,
2470 cellStateInInfo,
2471 scratchBufferInfo,
2472 outputStateOutInfo,
2473 cellStateOutInfo,
2474 outputInfo,
2475 desc,
2476 paramsInfo);
2477 if (!isSupported)
2478 {
2479 return false;
2480 }
2481
2482 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002483 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002484
2485 input.Connect(layer->GetInputSlot(0));
2486 outputStateIn.Connect(layer->GetInputSlot(1));
2487 cellStateIn.Connect(layer->GetInputSlot(2));
2488
2489 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
2490 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2491 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2492 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2493}
2494
Sadik Armagan701d9a02019-09-04 15:16:18 +01002495bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2496{
2497 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002498 ActivationDescriptor desc;
2499 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002500
2501 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2502}
2503
Mike Kelly46272802019-08-14 17:00:48 +01002504bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2505{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002506 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002507 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2508}
2509
2510bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2511{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002512 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002513 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2514}
2515
2516bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2517{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002518 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002519 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2520}
2521
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002522bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002523{
2524 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2525
2526 if (!input.IsValid())
2527 {
2528 return Fail("%s: Operation has invalid inputs", __func__);
2529 }
2530
2531 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2532
2533 if (!output)
2534 {
2535 return Fail("%s: Could not read output 0", __func__);
2536 }
2537
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002538 const TensorInfo& inputInfo = input.GetTensorInfo();
2539 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002540 if (IsDynamicTensor(outputInfo))
2541 {
2542 return Fail("%s: Dynamic output tensors are not supported", __func__);
2543 }
2544
2545 // ArmNN does not currently support non-fixed weights or bias
2546 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2547 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2548
2549 if (weightsOperand == nullptr)
2550 {
2551 return Fail("%s: Operand is invalid", __func__);
2552 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002553 TransposeConvolution2dDescriptor desc;
2554 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002555
2556 // Determine whether padding is implicit or explicit
2557 bool implicitPadding = operation.inputs.size() == 9;
2558
2559 if (implicitPadding )
2560 {
2561 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2562 }
2563 else
2564 {
2565 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2566 }
2567
2568 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2569 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2570 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2571
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002572 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002573
2574 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2575 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002576 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002577 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2578 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2579
2580 // Bias is a 1D tensor
2581 const ConstTensorPin biasPin =
2582 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2583
2584 if (!weightsPin.IsValid())
2585 {
2586 return Fail("%s: Operation has invalid weights", __func__);
2587 }
2588
2589 if (!biasPin.IsValid())
2590 {
2591 return Fail("%s: Operation has invalid biases", __func__);
2592 }
2593
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002594 ConstTensor weights = weightsPin.GetConstTensor();
2595 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002596 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2597
2598 ActivationFn activation;
2599
2600 if (implicitPadding)
2601 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002602 int32_t strideX{0};
2603 int32_t strideY{0};
2604 int32_t padLeft{0};
2605 int32_t padRight{0};
2606 int32_t padTop{0};
2607 int32_t padBottom{0};
2608
David Monahan613b49c2019-06-27 11:37:47 +01002609 android::nn::PaddingScheme paddingScheme;
2610 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002611 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2612 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002613 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2614 {
2615 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2616 }
2617
2618 const uint32_t kernelX = weights.GetShape()[widthIndex];
2619 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002620 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2621 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002622
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002623 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2624 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2625
2626 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2627 // but Arm NN only supports values >= 0
2628 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2629 {
2630 return Fail("%s: Negative padding values are not supported", __func__);
2631 }
2632
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002633 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2634 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002635 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2636 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2637 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2638 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002639 }
2640 else if (operation.inputs.size() == 11)
2641 {
2642 // explicit padding
2643 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2644 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2645 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2646 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2647 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2648 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2649 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2650 {
2651 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2652 }
2653 }
2654 else
2655 {
2656 return Fail("%s: Unsupported number of operation inputs", __func__);
2657 }
2658
2659 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002660 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002661
2662 bool isSupported = false;
2663 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2664 IsTransposeConvolution2dSupported,
2665 data.m_Backends,
2666 isSupported,
2667 inputInfo,
2668 outputInfo,
2669 desc,
2670 weights.GetInfo(),
2671 biases);
2672 if (!isSupported)
2673 {
2674 return false;
2675 }
2676
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002677 IConnectableLayer* startLayer =
2678 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002679 if (!startLayer)
2680 {
2681 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2682 }
2683
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002684 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002685 if (!endLayer)
2686 {
2687 return Fail("%s: ProcessActivation failed", __func__);
2688 }
2689
2690 input.Connect(startLayer->GetInputSlot(0));
2691
2692 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2693}
2694
Mike Kellyb5fdf382019-06-11 16:35:25 +01002695} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002696} // namespace armnn_driver