blob: 40dd55def266c661ed974065913fef5a4d421858 [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>
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010012
13#include <cmath>
14
Mike Kellyb5fdf382019-06-11 16:35:25 +010015namespace armnn_driver
16{
17namespace hal_1_2
18{
19
Mike Kellyb5fdf382019-06-11 16:35:25 +010020bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
21{
Mike Kellyb5fdf382019-06-11 16:35:25 +010022 switch (operation.type)
23 {
Kevin May407718f2019-09-09 14:46:41 +010024 case V1_2::OperationType::ABS:
25 return ConvertAbs(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010026 case V1_2::OperationType::ADD:
27 return ConvertAdd(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010028 case V1_2::OperationType::AVERAGE_POOL_2D:
29 return ConvertAveragePool2d(operation, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +010030 case V1_2::OperationType::BATCH_TO_SPACE_ND:
31 return ConvertBatchToSpaceNd(operation, model, data);
Mike Kellyb8805202019-07-31 17:25:43 +010032 case V1_2::OperationType::CONCATENATION:
33 return ConvertConcatenation(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010034 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010035 return ConvertConv2d(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010036 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010037 return ConvertDepthwiseConv2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010038 case V1_2::OperationType::DEQUANTIZE:
39 return ConvertDequantize(operation, model, data);
40 case V1_2::OperationType::DIV:
41 return ConvertDiv(operation, model, data);
42 case V1_2::OperationType::FLOOR:
43 return ConvertFloor(operation, model, data);
44 case V1_2::OperationType::FULLY_CONNECTED:
45 return ConvertFullyConnected(operation, model, data);
46 case V1_2::OperationType::L2_NORMALIZATION:
47 return ConvertL2Normalization(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010048 case V1_2::OperationType::L2_POOL_2D:
49 return ConvertL2Pool2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010050 case V1_2::OperationType::LOCAL_RESPONSE_NORMALIZATION:
51 return ConvertLocalResponseNormalization(operation, model, data);
52 case V1_2::OperationType::LOGISTIC:
53 return ConvertLogistic(operation, model, data);
54 case V1_2::OperationType::LSTM:
55 return ConvertLstm(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010056 case V1_2::OperationType::MAX_POOL_2D:
57 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +010058 case V1_2::OperationType::MAXIMUM:
59 return ConvertMaximum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010060 case V1_2::OperationType::MEAN:
61 return ConvertMean(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +010062 case V1_2::OperationType::MINIMUM:
63 return ConvertMinimum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010064 case V1_2::OperationType::MUL:
65 return ConvertMul(operation, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +010066 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +010067 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +010068 case V1_2::OperationType::PAD_V2:
69 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +010070 case V1_2::OperationType::PRELU:
71 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +010072 case V1_2::OperationType::QUANTIZE:
73 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +010074 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
75 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +010076 case V1_2::OperationType::RELU:
77 return ConvertReLu(operation, model, data);
78 case V1_2::OperationType::RELU1:
79 return ConvertReLu1(operation, model, data);
80 case V1_2::OperationType::RELU6:
81 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010082 case V1_2::OperationType::RESHAPE:
83 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +010084 case V1_2::OperationType::RESIZE_BILINEAR:
85 return ConvertResize(operation, model, data, armnn::ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010086 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +010087 return ConvertResize(operation, model, data, armnn::ResizeMethod::NearestNeighbor);
Sadik Armagan701d9a02019-09-04 15:16:18 +010088 case V1_2::OperationType::SQRT:
89 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010090 case V1_2::OperationType::SQUEEZE:
91 return ConvertSqueeze(operation, model, data);
92 case V1_2::OperationType::STRIDED_SLICE:
93 return ConvertStridedSlice(operation, model, data);
94 case V1_2::OperationType::TRANSPOSE:
95 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +010096 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +010097 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +010098 case V1_2::OperationType::SOFTMAX:
99 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +0100100 case V1_2::OperationType::SPACE_TO_BATCH_ND :
101 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100102 case V1_2::OperationType::SPACE_TO_DEPTH:
103 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100104 case V1_2::OperationType::SUB:
105 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100106 case V1_2::OperationType::TANH:
107 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100108 default:
109 return Fail("%s: Operation type %s not supported in ArmnnDriver",
110 __func__, toString(operation.type).c_str());
111 }
112}
113
Kevin May407718f2019-09-09 14:46:41 +0100114bool HalPolicy::ConvertAbs(const Operation& operation, const Model& model, ConversionData& data)
115{
116 ALOGV("hal_1_2::HalPolicy::ConvertAbs()");
117 return ::ConvertAbs<hal_1_2::HalPolicy>(operation, model, data);
118}
119
Mike Kelly46272802019-08-14 17:00:48 +0100120bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
121{
122 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
123 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
124}
125
Sadik Armagan15d63e22019-07-26 16:59:35 +0100126bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
127{
128 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
129 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Average, model, data);
130}
131
Finn Williams23b87b32019-07-30 11:44:05 +0100132bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
133{
134 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
135 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
136}
137
Mike Kellyb8805202019-07-31 17:25:43 +0100138bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
139{
140 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
141 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
142}
143
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100144bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
145{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100146 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
147
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100148 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
149 if (!input.IsValid())
150 {
151 return Fail("%s: Operation has invalid inputs", __func__);
152 }
153
154 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
155 if (!output)
156 {
157 return Fail("%s: Could not read output 0", __func__);
158 }
159
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100160 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
161 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
162
163 if (IsDynamicTensor(outputInfo))
164 {
165 return Fail("%s: Dynamic output tensors are not supported", __func__);
166 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100167
Mike Kellye1d60bb2019-07-11 11:44:52 +0100168 armnn::Convolution2dDescriptor desc;
169 desc.m_DataLayout = armnn::DataLayout::NHWC;
170
171 // Determine whether padding is implicit or explicit
172 bool implicitPadding = operation.inputs.size() == 7 ||
173 (operation.inputs.size() >= 8 &&
174 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
175
176 if (implicitPadding)
177 {
178 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
179 }
180 else if (operation.inputs.size() >= 10)
181 {
182 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
183 }
184
185 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
186
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100187 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100188 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
189 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
190 // the DataLayout is NCHW
191 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
192 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
193 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100194 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100195 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100196
197 if (!weightsPin.IsValid())
198 {
199 return Fail("%s: Operation has invalid weights", __func__);
200 }
201
202 if (!biasPin.IsValid())
203 {
204 return Fail("%s: Operation has invalid biases", __func__);
205 }
206
207 armnn::ConstTensor weights = weightsPin.GetConstTensor();
208 armnn::ConstTensor bias = biasPin.GetConstTensor();
209 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
210
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100211 ActivationFn activation;
212
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100213 if (implicitPadding)
214 {
215 android::nn::PaddingScheme paddingScheme;
216 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
217 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
218 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
219 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
220 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
221 {
222 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
223 }
224
Mike Kellye1d60bb2019-07-11 11:44:52 +0100225 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
226 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
227 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
228 const uint32_t kernelX = weights.GetShape()[widthIndex];
229 const uint32_t kernelY = weights.GetShape()[heightIndex];
230 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
231 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100232
Mike Kelly86b36d42019-07-12 16:39:33 +0100233 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
234 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100235
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100236 }
237 else if (operation.inputs.size() >= 10)
238 {
239 // explicit padding
240 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
241 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
242 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
243 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
244 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
245 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
246 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
247 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
248 {
249 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
250 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100251 }
252 else
253 {
254 return Fail("%s: Unsupported number of operation inputs", __func__);
255 }
256
257 desc.m_BiasEnabled = true;
258 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
259
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100260 bool isSupported = false;
261 FORWARD_LAYER_SUPPORT_FUNC(__func__,
262 IsConvolution2dSupported,
263 data.m_Backends,
264 isSupported,
265 inputInfo,
266 outputInfo,
267 desc,
268 weights.GetInfo(),
269 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100270
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100271 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100272 {
273 return false;
274 }
275
276 armnn::IConnectableLayer* startLayer =
277 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
278
279 if (!startLayer)
280 {
281 return Fail("%s: AddConvolution2dLayer failed", __func__);
282 }
283
284 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
285
286 if (!endLayer)
287 {
288 return Fail("%s: ProcessActivation failed", __func__);
289 }
290
291 input.Connect(startLayer->GetInputSlot(0));
292
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100293 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100294}
295
296bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
297{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100298 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
299
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100300 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
301
302 if (!input.IsValid())
303 {
304 return Fail("%s: Operation has invalid inputs", __func__);
305 }
306
307 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
308
309 if (!output)
310 {
311 return Fail("%s: Could not read output 0", __func__);
312 }
313
314 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100315 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
316
317 if (IsDynamicTensor(outputInfo))
318 {
319 return Fail("%s: Dynamic output tensors are not supported", __func__);
320 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100321
322 // ArmNN does not currently support non-fixed weights or bias
323 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
324 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
325
326 if (weightsOperand == nullptr)
327 {
328 return Fail("%s: Operand is invalid", __func__);
329 }
330 armnn::DepthwiseConvolution2dDescriptor desc;
331 desc.m_DataLayout = armnn::DataLayout::NHWC;
332
333 // Determine whether padding is implicit or explicit
334 bool implicitPadding = operation.inputs.size() == 8 ||
335 (operation.inputs.size() >= 9 &&
336 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
337
338 // Look ahead to find the optional DataLayout, if present
339 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
340 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
341
342 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
343 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
344 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
345 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
346
347 // Reinterpret weight data as [ H, W, I, M ]
348 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
349 weightsOperand->dimensions[2],
350 inputInfo.GetShape()[channelsIndex],
351 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
352
353 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
354 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
355
356 const ConstTensorPin weightsPin =
357 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
358 1,
359 model,
360 data,
361 HWIMToMIHW,
362 &weightsShape);
363
364 // Bias is a 1D tensor
365 const ConstTensorPin biasPin =
366 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
367
368 if (!weightsPin.IsValid())
369 {
370 return Fail("%s: Operation has invalid weights", __func__);
371 }
372
373 if (!biasPin.IsValid())
374 {
375 return Fail("%s: Operation has invalid biases", __func__);
376 }
377
378 armnn::ConstTensor weights = weightsPin.GetConstTensor();
379 armnn::ConstTensor bias = biasPin.GetConstTensor();
380 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
381
382 ActivationFn activation;
383
384 if (implicitPadding)
385 {
386 android::nn::PaddingScheme paddingScheme;
387 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
388 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
389 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
390 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
391 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
392 {
393 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
394 }
395
396 const uint32_t kernelX = weights.GetShape()[3];
397 const uint32_t kernelY = weights.GetShape()[2];
398 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
399 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
400
Mike Kelly86b36d42019-07-12 16:39:33 +0100401 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
402 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100403 }
404 else if (operation.inputs.size() >= 11)
405 {
406 // explicit padding
407 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
408 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
409 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
410 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
411 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
412 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
413 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
414 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
415 {
416 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
417 }
418 }
419 else
420 {
421 return Fail("%s: Unsupported number of operation inputs", __func__);
422 }
423
424 desc.m_BiasEnabled = true;
425 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
426
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100427 bool isSupported = false;
428 FORWARD_LAYER_SUPPORT_FUNC(__func__,
429 IsDepthwiseConvolutionSupported,
430 data.m_Backends,
431 isSupported,
432 inputInfo,
433 outputInfo,
434 desc,
435 weights.GetInfo(),
436 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100437
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100438 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100439 {
440 return false;
441 }
442
443 armnn::IConnectableLayer* startLayer =
444 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100445
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100446 if (!startLayer)
447 {
448 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
449 }
450
451 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
452 if (!endLayer)
453 {
454 return Fail("%s: ProcessActivation failed", __func__);
455 }
456
457 input.Connect(startLayer->GetInputSlot(0));
458
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100459 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100460}
461
Mike Kelly46272802019-08-14 17:00:48 +0100462bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
463{
464 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
465 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
466}
467
468bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
469{
470 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
471 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
472}
473
474bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
475{
476 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
477 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
478}
479
480bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
481{
482 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
483 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
484}
485
486bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
487{
488 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
489 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
490}
491
Sadik Armagan15d63e22019-07-26 16:59:35 +0100492bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
493{
494 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
495 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
496}
497
Mike Kelly46272802019-08-14 17:00:48 +0100498bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
499 const Model& model,
500 ConversionData& data)
501{
502 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
503 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
504}
505
506bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
507{
508 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
509 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
510}
511
Sadik Armagan15d63e22019-07-26 16:59:35 +0100512bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
513{
514 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
515 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
516}
517
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100518bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
519{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100520 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
521
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100522 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
523 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
524
525 if (!input0.IsValid() || !input1.IsValid())
526 {
527 return Fail("%s: Operation has invalid inputs", __func__);
528 }
529
530 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
531 if (!outputOperand)
532 {
533 return Fail("%s: Could not read output", __func__);
534 }
535
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100536 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100537 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100538 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100539 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100540 }
541
Aron Virginas-Tard7593232019-07-16 13:17:06 +0100542 bool isSupported = false;
543 FORWARD_LAYER_SUPPORT_FUNC(__func__,
544 IsMaximumSupported,
545 data.m_Backends,
546 isSupported,
547 input0.GetTensorInfo(),
548 input1.GetTensorInfo(),
549 outInfo);
550
551 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100552 {
553 return false;
554 }
555
556 armnn::IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
557 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100558 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
559 if (!isReshapeSupported)
560 {
561 return false;
562 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100563
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100564 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100565}
566
Mike Kelly46272802019-08-14 17:00:48 +0100567bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
568{
569 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
570 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
571}
572
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100573bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
574{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100575 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
576
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100577 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
578 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
579
580 if (!input0.IsValid() || !input1.IsValid())
581 {
582 return Fail("%s: Operation has invalid inputs", __func__);
583 }
584
585 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
586 if (!output)
587 {
588 return Fail("%s: Could not read output 0", __func__);
589 }
590
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100591 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100592 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100593 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100594 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100595 }
596
597 bool isSupported = false;
598 FORWARD_LAYER_SUPPORT_FUNC(__func__,
599 IsMinimumSupported,
600 data.m_Backends,
601 isSupported,
602 input0.GetTensorInfo(),
603 input1.GetTensorInfo(),
604 outputInfo);
605
606 if (!isSupported)
607 {
608 return false;
609 }
610
611 armnn::IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
612 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100613 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
614 if (!isReshapeSupported)
615 {
616 return false;
617 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100618
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100619 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100620}
621
Mike Kelly46272802019-08-14 17:00:48 +0100622bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
623{
624 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
625 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
626}
627
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100628bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
629{
630 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
631 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
632}
633
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100634bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
635{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100636 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
637
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100638 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
639 if (!input.IsValid())
640 {
641 return Fail("%s: Could not read input 0", __func__);
642 }
643
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100644 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
645 if (!output)
646 {
647 return Fail("%s: Could not read output", __func__);
648 }
649
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100650 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
651 unsigned int rank = inputInfo.GetNumDimensions();
652
653 armnn::PadDescriptor descriptor;
654 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
655 {
656 return Fail("%s: Could not convert paddings", __func__);
657 }
658
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100659 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100660 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100661 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100662 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100663 }
664
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100665 // Determine type of padding value
666 OperandType operandType0;
667 OperandType operandType2;
668
669 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
670 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
671 {
672 return Fail("%s: Operation has invalid inputs", __func__);
673 }
674
675 // Read value to use for padding
676 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
677 {
678 armnn::Half f16PadValue;
679 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
680 {
681 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
682 }
683
684 descriptor.m_PadValue = f16PadValue;
685 }
686 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
687 {
688 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
689 {
690 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
691 }
692 }
693 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
694 {
Mike Kelly3c673942019-07-25 09:26:06 +0100695 int32_t intPadValue = 0;
696 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100697 {
698 return Fail("%s: Could not read input 2 (INT32)", __func__);
699 }
Mike Kelly3c673942019-07-25 09:26:06 +0100700 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100701 }
702 else
703 {
704 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
705 }
706
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100707 bool isSupported = false;
708 FORWARD_LAYER_SUPPORT_FUNC(__func__,
709 IsPadSupported,
710 data.m_Backends,
711 isSupported,
712 inputInfo,
713 outputInfo,
714 descriptor);
715 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100716 {
717 return false;
718 }
719
720 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
721 assert(layer != nullptr);
722 input.Connect(layer->GetInputSlot(0));
723 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
724
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100725 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100726}
727
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100728bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
729{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100730 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
731
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100732 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
733 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
734
735 if (!input.IsValid() || !alpha.IsValid())
736 {
737 return Fail("%s: Operation has invalid inputs", __func__);
738 }
739
740 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
741
742 if (!output)
743 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100744 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100745 }
746
747 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
748 const armnn::TensorInfo& alphaInfo = alpha.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100749 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100750
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100751 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100752 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100753 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100754 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100755
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100756 bool isSupported = false;
757 FORWARD_LAYER_SUPPORT_FUNC(__func__,
758 IsPreluSupported,
759 data.m_Backends,
760 isSupported,
761 inputInfo,
762 alphaInfo,
763 outputInfo);
764 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100765 {
766 return false;
767 }
768
769 armnn::IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
770
771 if (!layer)
772 {
773 return Fail("%s: AddPreluLayer failed", __func__);
774 }
775
Sadik Armagan64b19b52019-08-19 09:49:58 +0100776 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
777 if (!isReshapeSupported)
778 {
779 return false;
780 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100781
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100782 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100783}
784
Sadik Armagan5a476a82019-07-30 09:43:18 +0100785bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
786{
787 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
788
789 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
790 if (!input.IsValid())
791 {
792 return Fail("%s: Operation has invalid input", __func__);
793 }
794
795 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
796 if (!outputOperand)
797 {
798 return Fail("%s: Operation has invalid outputs", __func__);
799 }
800
801 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
802 if (IsDynamicTensor(outputInfo))
803 {
804 return Fail("%s: Dynamic output tensors are not supported", __func__);
805 }
806
807 bool isSupported = false;
808 FORWARD_LAYER_SUPPORT_FUNC(__func__,
809 IsQuantizeSupported,
810 data.m_Backends,
811 isSupported,
812 input.GetTensorInfo(),
813 outputInfo);
814 if (!isSupported)
815 {
816 return false;
817 }
818
819 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
820 assert(layer != nullptr);
821 input.Connect(layer->GetInputSlot(0));
822
823 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
824}
825
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100826bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
827{
828 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
829
830 //Inputs:
831 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
832 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
833 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
834 if (!input.IsValid())
835 {
836 return Fail("%s: Could not read input 0: input", __func__);
837 }
838
839 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
840 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
841 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
842 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
843 if (!previousCellStateIn.IsValid())
844 {
845 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
846 }
847
848 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
849 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
850 // is quantized with a fixed quantization range of -1, 127/128.
851 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
852 if (!previousOutputIn.IsValid())
853 {
854 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
855 }
856
857 // Get the input tensors:
858 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
859 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
860 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
861 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100862 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100863
864 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
865 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
866 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
867 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100868 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100869
870 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
871 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
872 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
873 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100874 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100875
876 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
877 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
878 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
879 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100880 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100881
882 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
883 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
884 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
885 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100886 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100887
888 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
889 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
890 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
891 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100892 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100893
894 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
895 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
896 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
897 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100898 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100899
900 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
901 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
902 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
903 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100904 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100905
906 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
907 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
908 // of input and weights scales and zeroPoint equal to 0.
909 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100910 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100911
912 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
913 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
914 // of input and weights scales and zeroPoint equal to 0.
915 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100916 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100917
918 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
919 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
920 // and weights scales and zeroPoint equal to 0.
921 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100922 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100923
924 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
925 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
926 // of input and weights scales and zeroPoint equal to 0.
927 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100928 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100929
930 if (!inputToInputWeightsPin.IsValid() ||
931 !inputToForgetWeightsPin.IsValid() ||
932 !inputToCellWeightsPin.IsValid() ||
933 !inputToOutputWeightsPin.IsValid() ||
934 !recurrentToInputWeightsPin.IsValid() ||
935 !recurrentToForgetWeightsPin.IsValid() ||
936 !recurrentToCellWeightsPin.IsValid() ||
937 !recurrentToOutputWeightsPin.IsValid() ||
938 !inputGateBiasPin.IsValid() ||
939 !forgetGateBiasPin.IsValid() ||
940 !cellBiasPin.IsValid() ||
941 !outputGateBiasPin.IsValid())
942 {
943 return Fail("%s: Operation has invalid tensor inputs", __func__);
944 }
945
946 // Outputs:
947 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
948 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
949 // of -2^4, 2^4 * 32767/32768.
950 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
951 if (!cellStateOut)
952 {
953 return Fail("%s: Could not read output 0: cellStateOut", __func__);
954 }
955
956 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
957 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
958 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
959 if (!output)
960 {
961 return Fail("%s: Could not read output 1: output", __func__);
962 }
963
964 // Inputs
965 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
966 const armnn::TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
967 const armnn::TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
968
969 // Outputs
970 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
971 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
972
973 // Dynamic tensors currently not supported
974 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
975 {
976 return Fail("%s: Dynamic output tensors are not supported", __func__);
977 }
978
979 armnn::QuantizedLstmInputParams params;
980
981 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
982 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
983 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
984 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
985 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
986 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
987 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
988 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
989 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
990 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
991 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
992 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
993
994 armnn::QuantizedLstmInputParamsInfo paramsInfo;
995 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
996 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
997 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
998 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
999 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1000 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1001 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1002 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1003 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1004 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1005 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1006 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1007
1008 bool isSupported = false;
1009 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1010 IsQuantizedLstmSupported,
1011 data.m_Backends,
1012 isSupported,
1013 inputInfo,
1014 previousCellStateInInfo,
1015 previousOutputInInfo,
1016 cellStateOutInfo,
1017 outputInfo,
1018 paramsInfo);
1019
1020 if (!isSupported)
1021 {
1022 return false;
1023 }
1024
1025 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1026 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001027 previousCellStateIn.Connect(layer->GetInputSlot(1));
1028 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001029
1030 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1031 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1032}
1033
Sadik Armagan61113162019-07-25 09:09:40 +01001034bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1035{
1036 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1037 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1038}
1039
1040bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1041{
1042 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1043 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1044}
1045
1046bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1047{
1048 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1049 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1050}
1051
Mike Kelly46272802019-08-14 17:00:48 +01001052bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1053{
1054 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1055 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1056}
1057
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001058bool HalPolicy::ConvertResize(const Operation& operation,
1059 const Model& model,
1060 ConversionData& data,
1061 armnn::ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001062{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001063 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
1064
1065 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001066 if (!input.IsValid())
1067 {
1068 return Fail("%s: Could not read input 0", __func__);
1069 }
1070
1071 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1072 if (!output)
1073 {
1074 return Fail("%s: Could not read output 0", __func__);
1075 }
1076
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001077 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1078 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1079
1080 if (IsDynamicTensor(outputInfo))
1081 {
1082 return Fail("%s: Dynamic output tensors are not supported", __func__);
1083 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001084
1085 armnn::ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001086 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001087 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1088
1089 OperandType operandType1;
1090 OperandType operandType2;
1091
1092 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1093 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1094 {
1095 return Fail("%s: Operation has invalid inputs", __func__);
1096 }
1097
1098 if (operandType1 != operandType2)
1099 {
1100 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1101 }
1102
1103 if (operandType1 == OperandType::INT32)
1104 {
1105 // Case 1: resizing by shape
1106 int32_t targetWidth = 0;
1107 int32_t targetHeight = 0;
1108
1109 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1110 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1111 {
1112 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1113 }
1114
1115 if (targetWidth < 0 || targetHeight < 0)
1116 {
1117 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1118 "Target width/height cannot be < 0", __func__);
1119 }
1120
1121 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001122 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001123 }
1124 else if (operandType1 == OperandType::FLOAT32)
1125 {
1126 // Case 2: resizing by scale
1127 float widthScale = 1.0f;
1128 float heightScale = 1.0f;
1129
1130 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1131 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1132 {
1133 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1134 }
1135
1136 const armnn::TensorShape& inputShape = inputInfo.GetShape();
1137 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1138
1139 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1140 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1141
1142 descriptor.m_TargetWidth = std::floor(width * widthScale);
1143 descriptor.m_TargetHeight = std::floor(height * heightScale);
1144 }
1145 else
1146 {
1147 // NOTE: FLOAT16 scales are not supported
1148 return false;
1149 }
1150
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001151 bool isSupported = false;
1152 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1153 IsResizeSupported,
1154 data.m_Backends,
1155 isSupported,
1156 inputInfo,
1157 outputInfo,
1158 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001159
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001160 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001161 {
1162 return false;
1163 }
1164
1165 armnn::IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
1166
1167 assert(layer != nullptr);
1168
1169 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1170 input.Connect(layer->GetInputSlot(0));
1171
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001172 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001173}
1174
Finn Williamsd74c5052019-07-30 17:06:00 +01001175bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1176{
1177 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1178 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1179}
1180
Keith Davisa6bc52f2019-06-26 09:39:49 +01001181bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1182{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001183 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001184
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001185 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001186 if (!input.IsValid() )
1187 {
1188 return Fail("%s: Operation has invalid inputs", __func__);
1189 }
1190
1191 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1192 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001193 if (rank != 4)
1194 {
1195 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1196 }
1197
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001198 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1199 if (!output)
1200 {
1201 return Fail("%s: Could not read output 0", __func__);
1202 }
1203
1204 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1205 if (IsDynamicTensor(outputInfo))
1206 {
1207 return Fail("%s: Dynamic output tensors are not supported", __func__);
1208 }
1209
Keith Davisa6bc52f2019-06-26 09:39:49 +01001210 armnn::SpaceToDepthDescriptor desc;
1211
1212 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1213
1214 if (desc.m_BlockSize <= 1)
1215 {
1216 return Fail("%s: Block size must be at least 1 in all dimensions");
1217 }
1218
1219 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1220
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001221 bool isSupported = false;
1222 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1223 IsSpaceToDepthSupported,
1224 data.m_Backends,
1225 isSupported,
1226 inputInfo,
1227 outputInfo,
1228 desc);
1229 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01001230 {
1231 return false;
1232 }
1233
1234 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
1235 assert(layer != nullptr);
1236 input.Connect(layer->GetInputSlot(0));
1237
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001238 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001239}
1240
Francis Murtagh074c25a2019-07-22 16:40:57 +01001241bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1242{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001243 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1244
Francis Murtagh074c25a2019-07-22 16:40:57 +01001245 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1246 if (!input.IsValid())
1247 {
1248 return Fail("%s: Operation has invalid inputs", __func__);
1249 }
1250
1251 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1252 if (!outputOperand)
1253 {
1254 return Fail("%s: Operation has no outputs", __func__);
1255 }
1256
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001257 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001258 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001259 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001260 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001261 }
1262
1263 armnn::SoftmaxDescriptor desc;
1264 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1265 {
1266 return Fail("%s: Operation has invalid inputs", __func__);
1267 }
1268
1269 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1270 2,
1271 HalPolicy::OperandType::INT32,
1272 desc.m_Axis,
1273 model,
1274 data))
1275 {
1276 return Fail("%s: Operation has invalid inputs", __func__);
1277 }
1278
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01001279 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1280 !(desc.m_Axis == 1 ||
1281 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1282 {
1283 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1284 }
1285
Francis Murtagh074c25a2019-07-22 16:40:57 +01001286 bool isSupported = false;
1287 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1288 IsSoftmaxSupported,
1289 data.m_Backends,
1290 isSupported,
1291 input.GetTensorInfo(),
1292 outputInfo,
1293 desc);
1294 if (!isSupported)
1295 {
1296 return false;
1297 }
1298
1299 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1300 assert(layer != nullptr);
1301 input.Connect(layer->GetInputSlot(0));
1302
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001303 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001304}
1305
Mike Kelly0a879362019-07-29 16:56:31 +01001306bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
1307{
1308 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
1309 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
1310}
1311
Sadik Armagan61113162019-07-25 09:09:40 +01001312bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1313{
1314 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
1315 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
1316}
1317
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001318bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
1319{
1320 // Inputs:
1321 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
1322 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
1323 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1324 if (!input.IsValid())
1325 {
1326 return Fail("%s: Could not read input 0: input", __func__);
1327 }
1328 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1329 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
1330 if (!outputStateIn.IsValid())
1331 {
1332 return Fail("%s: Could not read input 18: outputStateIn", __func__);
1333 }
1334 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1335 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
1336 if (!cellStateIn.IsValid())
1337 {
1338 return Fail("%s: Could not read input 19: cellStateIn", __func__);
1339 }
1340
1341 // Get the mandatory input tensors:
1342 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1343 // [num_units, input_size].
1344 const ConstTensorPin inputToForgetWeightsPin =
1345 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1346 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1347 // [num_units, input_size].
1348 const ConstTensorPin inputToCellWeightsPin =
1349 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
1350 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1351 // [num_units, input_size].
1352 const ConstTensorPin inputToOutputWeightsPin =
1353 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
1354 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1355 // [num_units, output_size].
1356 const ConstTensorPin recurrentToForgetWeightsPin =
1357 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
1358 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1359 // [num_units, output_size].
1360 const ConstTensorPin recurrentToCellWeightsPin =
1361 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
1362 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1363 // [num_units, output_size].
1364 const ConstTensorPin recurrentToOutputWeightsPin =
1365 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
1366 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1367 const ConstTensorPin forgetGateBiasPin =
1368 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
1369 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1370 const ConstTensorPin cellBiasPin =
1371 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
1372 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1373 const ConstTensorPin outputGateBiasPin =
1374 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
1375
1376 if (!inputToForgetWeightsPin.IsValid() ||
1377 !inputToCellWeightsPin.IsValid() ||
1378 !inputToOutputWeightsPin.IsValid() ||
1379 !recurrentToForgetWeightsPin.IsValid() ||
1380 !recurrentToCellWeightsPin.IsValid() ||
1381 !recurrentToOutputWeightsPin.IsValid() ||
1382 !forgetGateBiasPin.IsValid() ||
1383 !cellBiasPin.IsValid() ||
1384 !outputGateBiasPin.IsValid())
1385 {
1386 return Fail("%s: Operation has invalid tensor inputs", __func__);
1387 }
1388
1389 // Get the optional input tensors:
1390 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1391 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
1392 const ConstTensorPin inputToInputWeightsPin =
1393 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1394 1,
1395 model,
1396 data,
1397 g_DontPermute,
1398 nullptr,
1399 true);
1400
1401 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1402 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
1403 // “num_units”), or the second dimension of the “projection_weights”, if defined.
1404 const ConstTensorPin recurrentToInputWeightsPin =
1405 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1406 5,
1407 model,
1408 data,
1409 g_DontPermute,
1410 nullptr,
1411 true);
1412
1413 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1414 const ConstTensorPin cellToInputWeightsPin =
1415 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1416 9,
1417 model,
1418 data,
1419 g_DontPermute,
1420 nullptr,
1421 true);
1422
1423 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1424 const ConstTensorPin cellToForgetWeightsPin =
1425 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1426 10,
1427 model,
1428 data,
1429 g_DontPermute,
1430 nullptr,
1431 true);
1432
1433 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1434 const ConstTensorPin cellToOutputWeightsPin =
1435 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1436 11,
1437 model,
1438 data,
1439 g_DontPermute,
1440 nullptr,
1441 true);
1442
1443 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1444 const ConstTensorPin inputGateBiasPin =
1445 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1446 12,
1447 model,
1448 data,
1449 g_DontPermute,
1450 nullptr,
1451 true);
1452
1453 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1454 // [output_size, num_units].
1455 const ConstTensorPin projectionWeightsPin =
1456 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1457 16,
1458 model,
1459 data,
1460 g_DontPermute,
1461 nullptr,
1462 true);
1463
1464 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
1465 const ConstTensorPin projectionBiasPin =
1466 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1467 17,
1468 model,
1469 data,
1470 g_DontPermute,
1471 nullptr,
1472 true);
1473
1474 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
1475 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
1476 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
1477 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
1478 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
1479 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
1480 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
1481 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
1482 {
1483 return Fail("%s: Operation has invalid tensor inputs", __func__);
1484 }
1485
1486 // Get the mandatory input scalars (actually 1-D tensors of size 1):
1487 // 20: The activation function: A value indicating the activation function:
1488 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
1489 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
1490 // If set to 0.0 then clipping is disabled.
1491 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
1492 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
1493 ActivationFn activation;
1494 float cellClip;
1495 float projClip;
1496 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
1497 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
1498 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
1499 {
1500 return Fail("%s: Operation has invalid scalar inputs", __func__);
1501 }
1502
1503 // Get the normalization tensors
1504 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
1505 // Used to rescale normalized inputs to activation at input gate.
1506 const ConstTensorPin inputLayerNormWeightsPin =
1507 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1508 23,
1509 model,
1510 data,
1511 g_DontPermute,
1512 nullptr,
1513 true);
1514
1515 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
1516 // Used to rescale normalized inputs to activation at forget gate.
1517 const ConstTensorPin forgetLayerNormWeightsPin =
1518 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1519 24,
1520 model,
1521 data,
1522 g_DontPermute,
1523 nullptr,
1524 true);
1525
1526 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
1527 // Used to rescale normalized inputs to activation at cell gate.
1528 const ConstTensorPin cellLayerNormWeightsPin =
1529 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1530 25,
1531 model,
1532 data,
1533 g_DontPermute,
1534 nullptr,
1535 true);
1536
1537 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
1538 // Used to rescale normalized inputs to activation at output gate.
1539 const ConstTensorPin outputLayerNormWeightsPin =
1540 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1541 26,
1542 model,
1543 data,
1544 g_DontPermute,
1545 nullptr,
1546 true);
1547
1548 // Outputs:
1549 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
1550 // with CIFG, or [batch_size, num_units * 3] without CIFG.
1551 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1552 if (!scratchBuffer)
1553 {
1554 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
1555 }
1556 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1557 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1558 if (!outputStateOut)
1559 {
1560 return Fail("%s: Could not read output 1: outputStateOut", __func__);
1561 }
1562 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1563 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
1564 if (!cellStateOut)
1565 {
1566 return Fail("%s: Could not read output 2: cellStateOut", __func__);
1567 }
1568 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
1569 // effectively the same as the current “output state (out)” value.
1570 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
1571 if (!output)
1572 {
1573 return Fail("%s: Could not read output 3: output", __func__);
1574 }
1575
1576 // set the params structure for the AddLstmLayer call
1577 armnn::LstmInputParams params;
1578 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1579 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1580 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1581 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1582 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1583 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1584 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1585 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1586 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
1587 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
1588 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
1589 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1590 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1591 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1592 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1593 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
1594 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
1595 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
1596 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
1597 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
1598 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
1599
1600 // set the layer descriptor
1601 armnn::LstmDescriptor desc;
1602 desc.m_ActivationFunc = activation;
1603 desc.m_ClippingThresCell = cellClip;
1604 desc.m_ClippingThresProj = projClip;
1605 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
1606 params.m_RecurrentToInputWeights == nullptr ||
1607 params.m_InputGateBias == nullptr);
1608 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
1609 params.m_CellToOutputWeights != nullptr);
1610 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
1611 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
1612 params.m_ForgetLayerNormWeights != nullptr ||
1613 params.m_CellLayerNormWeights != nullptr ||
1614 params.m_OutputLayerNormWeights != nullptr);
1615
1616 // validate the optional input groups
1617 if (desc.m_CifgEnabled &&
1618 (params.m_InputToInputWeights != nullptr ||
1619 params.m_RecurrentToInputWeights != nullptr ||
1620 params.m_InputGateBias != nullptr))
1621 {
1622 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
1623 " and input gate bias must be provided", __func__);
1624 }
1625
1626 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
1627 {
1628 return Fail("%s: projection bias should not be provided without projection weights", __func__);
1629 }
1630
1631 if (desc.m_PeepholeEnabled &&
1632 (params.m_CellToForgetWeights == nullptr ||
1633 params.m_CellToOutputWeights == nullptr ||
1634 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
1635 {
1636 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
1637 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
1638 }
1639
1640 if (desc.m_LayerNormEnabled &&
1641 (params.m_ForgetLayerNormWeights == nullptr ||
1642 params.m_CellLayerNormWeights == nullptr ||
1643 params.m_OutputLayerNormWeights == nullptr ||
1644 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
1645 {
1646 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
1647 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
1648 }
1649
1650 // Check if the layer is supported
1651 // Inputs
1652 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1653 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
1654 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
1655
1656 // Outputs
1657 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
1658 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
1659 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1660 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1661
Ferran Balaguera4a629a2019-07-30 10:16:13 +01001662 if (IsDynamicTensor(scratchBufferInfo) ||
1663 IsDynamicTensor(outputStateOutInfo) ||
1664 IsDynamicTensor(cellStateOutInfo) ||
1665 IsDynamicTensor(outputInfo))
1666 {
1667 return Fail("%s: Dynamic output tensors are not supported", __func__);
1668 }
1669
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001670 // Basic parameters
1671 armnn::LstmInputParamsInfo paramsInfo;
1672 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1673 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1674 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1675 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1676 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1677 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1678 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1679 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1680 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1681
1682 // Optional parameters
1683 if(!desc.m_CifgEnabled)
1684 {
1685 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1686 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1687 if (params.m_CellToInputWeights != nullptr)
1688 {
1689 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
1690 }
1691 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1692 }
1693
1694 if(desc.m_ProjectionEnabled)
1695 {
1696 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
1697 if (params.m_ProjectionBias != nullptr)
1698 {
1699 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
1700 }
1701 }
1702
1703 if(desc.m_PeepholeEnabled)
1704 {
1705 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
1706 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
1707 }
1708
1709 if (desc.m_LayerNormEnabled)
1710 {
1711 if(!desc.m_CifgEnabled)
1712 {
1713 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
1714 }
1715 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
1716 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
1717 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
1718 }
1719
1720 bool isSupported = false;
1721 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1722 IsLstmSupported,
1723 data.m_Backends,
1724 isSupported,
1725 inputInfo,
1726 outputStateInInfo,
1727 cellStateInInfo,
1728 scratchBufferInfo,
1729 outputStateOutInfo,
1730 cellStateOutInfo,
1731 outputInfo,
1732 desc,
1733 paramsInfo);
1734 if (!isSupported)
1735 {
1736 return false;
1737 }
1738
1739 // Add the layer
1740 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
1741
1742 input.Connect(layer->GetInputSlot(0));
1743 outputStateIn.Connect(layer->GetInputSlot(1));
1744 cellStateIn.Connect(layer->GetInputSlot(2));
1745
1746 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1747 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
1748 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
1749 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
1750}
1751
Sadik Armagan701d9a02019-09-04 15:16:18 +01001752bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
1753{
1754 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
1755 armnn::ActivationDescriptor desc;
1756 desc.m_Function = armnn::ActivationFunction::Sqrt;
1757
1758 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
1759}
1760
Mike Kelly46272802019-08-14 17:00:48 +01001761bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
1762{
Sadik Armagan701d9a02019-09-04 15:16:18 +01001763 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01001764 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
1765}
1766
1767bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
1768{
Sadik Armagan701d9a02019-09-04 15:16:18 +01001769 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01001770 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
1771}
1772
1773bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
1774{
Sadik Armagan701d9a02019-09-04 15:16:18 +01001775 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01001776 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
1777}
1778
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01001779bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01001780{
1781 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1782
1783 if (!input.IsValid())
1784 {
1785 return Fail("%s: Operation has invalid inputs", __func__);
1786 }
1787
1788 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1789
1790 if (!output)
1791 {
1792 return Fail("%s: Could not read output 0", __func__);
1793 }
1794
1795 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1796 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1797 if (IsDynamicTensor(outputInfo))
1798 {
1799 return Fail("%s: Dynamic output tensors are not supported", __func__);
1800 }
1801
1802 // ArmNN does not currently support non-fixed weights or bias
1803 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
1804 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1805
1806 if (weightsOperand == nullptr)
1807 {
1808 return Fail("%s: Operand is invalid", __func__);
1809 }
1810 armnn::TransposeConvolution2dDescriptor desc;
1811 desc.m_DataLayout = armnn::DataLayout::NHWC;
1812
1813 // Determine whether padding is implicit or explicit
1814 bool implicitPadding = operation.inputs.size() == 9;
1815
1816 if (implicitPadding )
1817 {
1818 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
1819 }
1820 else
1821 {
1822 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
1823 }
1824
1825 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
1826 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1827 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1828
1829 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
1830
1831 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
1832 // We have to permute it to OIHW if the data layout is NCHW.
1833 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
1834 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
1835 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
1836
1837 // Bias is a 1D tensor
1838 const ConstTensorPin biasPin =
1839 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1840
1841 if (!weightsPin.IsValid())
1842 {
1843 return Fail("%s: Operation has invalid weights", __func__);
1844 }
1845
1846 if (!biasPin.IsValid())
1847 {
1848 return Fail("%s: Operation has invalid biases", __func__);
1849 }
1850
1851 armnn::ConstTensor weights = weightsPin.GetConstTensor();
1852 armnn::ConstTensor bias = biasPin.GetConstTensor();
1853 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1854
1855 ActivationFn activation;
1856
1857 if (implicitPadding)
1858 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001859 int32_t strideX{0};
1860 int32_t strideY{0};
1861 int32_t padLeft{0};
1862 int32_t padRight{0};
1863 int32_t padTop{0};
1864 int32_t padBottom{0};
1865
David Monahan613b49c2019-06-27 11:37:47 +01001866 android::nn::PaddingScheme paddingScheme;
1867 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001868 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
1869 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01001870 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
1871 {
1872 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1873 }
1874
1875 const uint32_t kernelX = weights.GetShape()[widthIndex];
1876 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001877 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
1878 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01001879
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001880 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
1881 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
1882
1883 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
1884 // but Arm NN only supports values >= 0
1885 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
1886 {
1887 return Fail("%s: Negative padding values are not supported", __func__);
1888 }
1889
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001890 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
1891 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001892 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
1893 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
1894 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
1895 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01001896 }
1897 else if (operation.inputs.size() == 11)
1898 {
1899 // explicit padding
1900 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
1901 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
1902 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
1903 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
1904 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
1905 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
1906 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
1907 {
1908 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1909 }
1910 }
1911 else
1912 {
1913 return Fail("%s: Unsupported number of operation inputs", __func__);
1914 }
1915
1916 desc.m_BiasEnabled = true;
1917 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1918
1919 bool isSupported = false;
1920 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1921 IsTransposeConvolution2dSupported,
1922 data.m_Backends,
1923 isSupported,
1924 inputInfo,
1925 outputInfo,
1926 desc,
1927 weights.GetInfo(),
1928 biases);
1929 if (!isSupported)
1930 {
1931 return false;
1932 }
1933
1934 armnn::IConnectableLayer* startLayer =
1935 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1936 if (!startLayer)
1937 {
1938 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
1939 }
1940
1941 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1942 if (!endLayer)
1943 {
1944 return Fail("%s: ProcessActivation failed", __func__);
1945 }
1946
1947 input.Connect(startLayer->GetInputSlot(0));
1948
1949 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
1950}
1951
Mike Kellyb5fdf382019-06-11 16:35:25 +01001952} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001953} // namespace armnn_driver