blob: 7bad22dbdeae5376dec512813c7055952e67d994 [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 {
Mike Kelly46272802019-08-14 17:00:48 +010024 case V1_2::OperationType::ADD:
25 return ConvertAdd(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010026 case V1_2::OperationType::AVERAGE_POOL_2D:
27 return ConvertAveragePool2d(operation, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +010028 case V1_2::OperationType::BATCH_TO_SPACE_ND:
29 return ConvertBatchToSpaceNd(operation, model, data);
Mike Kellyb8805202019-07-31 17:25:43 +010030 case V1_2::OperationType::CONCATENATION:
31 return ConvertConcatenation(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010032 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010033 return ConvertConv2d(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010034 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010035 return ConvertDepthwiseConv2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010036 case V1_2::OperationType::DEQUANTIZE:
37 return ConvertDequantize(operation, model, data);
38 case V1_2::OperationType::DIV:
39 return ConvertDiv(operation, model, data);
40 case V1_2::OperationType::FLOOR:
41 return ConvertFloor(operation, model, data);
42 case V1_2::OperationType::FULLY_CONNECTED:
43 return ConvertFullyConnected(operation, model, data);
44 case V1_2::OperationType::L2_NORMALIZATION:
45 return ConvertL2Normalization(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010046 case V1_2::OperationType::L2_POOL_2D:
47 return ConvertL2Pool2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010048 case V1_2::OperationType::LOCAL_RESPONSE_NORMALIZATION:
49 return ConvertLocalResponseNormalization(operation, model, data);
50 case V1_2::OperationType::LOGISTIC:
51 return ConvertLogistic(operation, model, data);
52 case V1_2::OperationType::LSTM:
53 return ConvertLstm(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010054 case V1_2::OperationType::MAX_POOL_2D:
55 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +010056 case V1_2::OperationType::MAXIMUM:
57 return ConvertMaximum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010058 case V1_2::OperationType::MEAN:
59 return ConvertMean(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +010060 case V1_2::OperationType::MINIMUM:
61 return ConvertMinimum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010062 case V1_2::OperationType::MUL:
63 return ConvertMul(operation, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +010064 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +010065 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +010066 case V1_2::OperationType::PAD_V2:
67 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +010068 case V1_2::OperationType::PRELU:
69 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +010070 case V1_2::OperationType::QUANTIZE:
71 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +010072 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
73 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +010074 case V1_2::OperationType::RELU:
75 return ConvertReLu(operation, model, data);
76 case V1_2::OperationType::RELU1:
77 return ConvertReLu1(operation, model, data);
78 case V1_2::OperationType::RELU6:
79 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010080 case V1_2::OperationType::RESHAPE:
81 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +010082 case V1_2::OperationType::RESIZE_BILINEAR:
83 return ConvertResize(operation, model, data, armnn::ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010084 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +010085 return ConvertResize(operation, model, data, armnn::ResizeMethod::NearestNeighbor);
Sadik Armagan701d9a02019-09-04 15:16:18 +010086 case V1_2::OperationType::SQRT:
87 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010088 case V1_2::OperationType::SQUEEZE:
89 return ConvertSqueeze(operation, model, data);
90 case V1_2::OperationType::STRIDED_SLICE:
91 return ConvertStridedSlice(operation, model, data);
92 case V1_2::OperationType::TRANSPOSE:
93 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +010094 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +010095 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +010096 case V1_2::OperationType::SOFTMAX:
97 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +010098 case V1_2::OperationType::SPACE_TO_BATCH_ND :
99 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100100 case V1_2::OperationType::SPACE_TO_DEPTH:
101 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100102 case V1_2::OperationType::SUB:
103 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100104 case V1_2::OperationType::TANH:
105 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100106 default:
107 return Fail("%s: Operation type %s not supported in ArmnnDriver",
108 __func__, toString(operation.type).c_str());
109 }
110}
111
Mike Kelly46272802019-08-14 17:00:48 +0100112bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
113{
114 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
115 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
116}
117
Sadik Armagan15d63e22019-07-26 16:59:35 +0100118bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
119{
120 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
121 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Average, model, data);
122}
123
Finn Williams23b87b32019-07-30 11:44:05 +0100124bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
125{
126 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
127 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
128}
129
Mike Kellyb8805202019-07-31 17:25:43 +0100130bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
131{
132 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
133 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
134}
135
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100136bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
137{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100138 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
139
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100140 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
141 if (!input.IsValid())
142 {
143 return Fail("%s: Operation has invalid inputs", __func__);
144 }
145
146 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
147 if (!output)
148 {
149 return Fail("%s: Could not read output 0", __func__);
150 }
151
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100152 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
153 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
154
155 if (IsDynamicTensor(outputInfo))
156 {
157 return Fail("%s: Dynamic output tensors are not supported", __func__);
158 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100159
Mike Kellye1d60bb2019-07-11 11:44:52 +0100160 armnn::Convolution2dDescriptor desc;
161 desc.m_DataLayout = armnn::DataLayout::NHWC;
162
163 // Determine whether padding is implicit or explicit
164 bool implicitPadding = operation.inputs.size() == 7 ||
165 (operation.inputs.size() >= 8 &&
166 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
167
168 if (implicitPadding)
169 {
170 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
171 }
172 else if (operation.inputs.size() >= 10)
173 {
174 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
175 }
176
177 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
178
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100179 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100180 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
181 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
182 // the DataLayout is NCHW
183 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
184 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
185 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100186 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100187 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100188
189 if (!weightsPin.IsValid())
190 {
191 return Fail("%s: Operation has invalid weights", __func__);
192 }
193
194 if (!biasPin.IsValid())
195 {
196 return Fail("%s: Operation has invalid biases", __func__);
197 }
198
199 armnn::ConstTensor weights = weightsPin.GetConstTensor();
200 armnn::ConstTensor bias = biasPin.GetConstTensor();
201 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
202
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100203 ActivationFn activation;
204
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100205 if (implicitPadding)
206 {
207 android::nn::PaddingScheme paddingScheme;
208 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
209 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
210 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
211 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
212 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
213 {
214 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
215 }
216
Mike Kellye1d60bb2019-07-11 11:44:52 +0100217 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
218 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
219 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
220 const uint32_t kernelX = weights.GetShape()[widthIndex];
221 const uint32_t kernelY = weights.GetShape()[heightIndex];
222 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
223 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100224
Mike Kelly86b36d42019-07-12 16:39:33 +0100225 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
226 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100227
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100228 }
229 else if (operation.inputs.size() >= 10)
230 {
231 // explicit padding
232 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
233 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
234 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
235 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
236 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
237 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
238 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
239 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
240 {
241 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
242 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100243 }
244 else
245 {
246 return Fail("%s: Unsupported number of operation inputs", __func__);
247 }
248
249 desc.m_BiasEnabled = true;
250 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
251
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100252 bool isSupported = false;
253 FORWARD_LAYER_SUPPORT_FUNC(__func__,
254 IsConvolution2dSupported,
255 data.m_Backends,
256 isSupported,
257 inputInfo,
258 outputInfo,
259 desc,
260 weights.GetInfo(),
261 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100262
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100263 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100264 {
265 return false;
266 }
267
268 armnn::IConnectableLayer* startLayer =
269 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
270
271 if (!startLayer)
272 {
273 return Fail("%s: AddConvolution2dLayer failed", __func__);
274 }
275
276 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
277
278 if (!endLayer)
279 {
280 return Fail("%s: ProcessActivation failed", __func__);
281 }
282
283 input.Connect(startLayer->GetInputSlot(0));
284
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100285 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100286}
287
288bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
289{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100290 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
291
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100292 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
293
294 if (!input.IsValid())
295 {
296 return Fail("%s: Operation has invalid inputs", __func__);
297 }
298
299 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
300
301 if (!output)
302 {
303 return Fail("%s: Could not read output 0", __func__);
304 }
305
306 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100307 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
308
309 if (IsDynamicTensor(outputInfo))
310 {
311 return Fail("%s: Dynamic output tensors are not supported", __func__);
312 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100313
314 // ArmNN does not currently support non-fixed weights or bias
315 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
316 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
317
318 if (weightsOperand == nullptr)
319 {
320 return Fail("%s: Operand is invalid", __func__);
321 }
322 armnn::DepthwiseConvolution2dDescriptor desc;
323 desc.m_DataLayout = armnn::DataLayout::NHWC;
324
325 // Determine whether padding is implicit or explicit
326 bool implicitPadding = operation.inputs.size() == 8 ||
327 (operation.inputs.size() >= 9 &&
328 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
329
330 // Look ahead to find the optional DataLayout, if present
331 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
332 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
333
334 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
335 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
336 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
337 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
338
339 // Reinterpret weight data as [ H, W, I, M ]
340 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
341 weightsOperand->dimensions[2],
342 inputInfo.GetShape()[channelsIndex],
343 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
344
345 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
346 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
347
348 const ConstTensorPin weightsPin =
349 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
350 1,
351 model,
352 data,
353 HWIMToMIHW,
354 &weightsShape);
355
356 // Bias is a 1D tensor
357 const ConstTensorPin biasPin =
358 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
359
360 if (!weightsPin.IsValid())
361 {
362 return Fail("%s: Operation has invalid weights", __func__);
363 }
364
365 if (!biasPin.IsValid())
366 {
367 return Fail("%s: Operation has invalid biases", __func__);
368 }
369
370 armnn::ConstTensor weights = weightsPin.GetConstTensor();
371 armnn::ConstTensor bias = biasPin.GetConstTensor();
372 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
373
374 ActivationFn activation;
375
376 if (implicitPadding)
377 {
378 android::nn::PaddingScheme paddingScheme;
379 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
380 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
381 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
382 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
383 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
384 {
385 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
386 }
387
388 const uint32_t kernelX = weights.GetShape()[3];
389 const uint32_t kernelY = weights.GetShape()[2];
390 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
391 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
392
Mike Kelly86b36d42019-07-12 16:39:33 +0100393 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
394 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100395 }
396 else if (operation.inputs.size() >= 11)
397 {
398 // explicit padding
399 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
400 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
401 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
402 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
403 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
404 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
405 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
406 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
407 {
408 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
409 }
410 }
411 else
412 {
413 return Fail("%s: Unsupported number of operation inputs", __func__);
414 }
415
416 desc.m_BiasEnabled = true;
417 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
418
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100419 bool isSupported = false;
420 FORWARD_LAYER_SUPPORT_FUNC(__func__,
421 IsDepthwiseConvolutionSupported,
422 data.m_Backends,
423 isSupported,
424 inputInfo,
425 outputInfo,
426 desc,
427 weights.GetInfo(),
428 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100429
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100430 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100431 {
432 return false;
433 }
434
435 armnn::IConnectableLayer* startLayer =
436 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100437
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100438 if (!startLayer)
439 {
440 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
441 }
442
443 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
444 if (!endLayer)
445 {
446 return Fail("%s: ProcessActivation failed", __func__);
447 }
448
449 input.Connect(startLayer->GetInputSlot(0));
450
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100451 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100452}
453
Mike Kelly46272802019-08-14 17:00:48 +0100454bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
455{
456 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
457 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
458}
459
460bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
461{
462 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
463 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
464}
465
466bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
467{
468 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
469 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
470}
471
472bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
473{
474 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
475 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
476}
477
478bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
479{
480 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
481 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
482}
483
Sadik Armagan15d63e22019-07-26 16:59:35 +0100484bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
485{
486 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
487 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
488}
489
Mike Kelly46272802019-08-14 17:00:48 +0100490bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
491 const Model& model,
492 ConversionData& data)
493{
494 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
495 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
496}
497
498bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
499{
500 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
501 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
502}
503
Sadik Armagan15d63e22019-07-26 16:59:35 +0100504bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
505{
506 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
507 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
508}
509
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100510bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
511{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100512 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
513
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100514 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
515 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
516
517 if (!input0.IsValid() || !input1.IsValid())
518 {
519 return Fail("%s: Operation has invalid inputs", __func__);
520 }
521
522 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
523 if (!outputOperand)
524 {
525 return Fail("%s: Could not read output", __func__);
526 }
527
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100528 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100529 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100530 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100531 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100532 }
533
Aron Virginas-Tard7593232019-07-16 13:17:06 +0100534 bool isSupported = false;
535 FORWARD_LAYER_SUPPORT_FUNC(__func__,
536 IsMaximumSupported,
537 data.m_Backends,
538 isSupported,
539 input0.GetTensorInfo(),
540 input1.GetTensorInfo(),
541 outInfo);
542
543 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100544 {
545 return false;
546 }
547
548 armnn::IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
549 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100550 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
551 if (!isReshapeSupported)
552 {
553 return false;
554 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100555
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100556 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100557}
558
Mike Kelly46272802019-08-14 17:00:48 +0100559bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
560{
561 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
562 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
563}
564
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100565bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
566{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100567 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
568
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100569 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
570 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
571
572 if (!input0.IsValid() || !input1.IsValid())
573 {
574 return Fail("%s: Operation has invalid inputs", __func__);
575 }
576
577 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
578 if (!output)
579 {
580 return Fail("%s: Could not read output 0", __func__);
581 }
582
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100583 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100584 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100585 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100586 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100587 }
588
589 bool isSupported = false;
590 FORWARD_LAYER_SUPPORT_FUNC(__func__,
591 IsMinimumSupported,
592 data.m_Backends,
593 isSupported,
594 input0.GetTensorInfo(),
595 input1.GetTensorInfo(),
596 outputInfo);
597
598 if (!isSupported)
599 {
600 return false;
601 }
602
603 armnn::IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
604 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100605 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
606 if (!isReshapeSupported)
607 {
608 return false;
609 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100610
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100611 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100612}
613
Mike Kelly46272802019-08-14 17:00:48 +0100614bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
615{
616 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
617 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
618}
619
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100620bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
621{
622 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
623 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
624}
625
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100626bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
627{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100628 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
629
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100630 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
631 if (!input.IsValid())
632 {
633 return Fail("%s: Could not read input 0", __func__);
634 }
635
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100636 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
637 if (!output)
638 {
639 return Fail("%s: Could not read output", __func__);
640 }
641
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100642 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
643 unsigned int rank = inputInfo.GetNumDimensions();
644
645 armnn::PadDescriptor descriptor;
646 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
647 {
648 return Fail("%s: Could not convert paddings", __func__);
649 }
650
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100651 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100652 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100653 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100654 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100655 }
656
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100657 // Determine type of padding value
658 OperandType operandType0;
659 OperandType operandType2;
660
661 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
662 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
663 {
664 return Fail("%s: Operation has invalid inputs", __func__);
665 }
666
667 // Read value to use for padding
668 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
669 {
670 armnn::Half f16PadValue;
671 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
672 {
673 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
674 }
675
676 descriptor.m_PadValue = f16PadValue;
677 }
678 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
679 {
680 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
681 {
682 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
683 }
684 }
685 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
686 {
Mike Kelly3c673942019-07-25 09:26:06 +0100687 int32_t intPadValue = 0;
688 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100689 {
690 return Fail("%s: Could not read input 2 (INT32)", __func__);
691 }
Mike Kelly3c673942019-07-25 09:26:06 +0100692 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100693 }
694 else
695 {
696 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
697 }
698
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100699 bool isSupported = false;
700 FORWARD_LAYER_SUPPORT_FUNC(__func__,
701 IsPadSupported,
702 data.m_Backends,
703 isSupported,
704 inputInfo,
705 outputInfo,
706 descriptor);
707 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100708 {
709 return false;
710 }
711
712 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
713 assert(layer != nullptr);
714 input.Connect(layer->GetInputSlot(0));
715 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
716
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100717 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100718}
719
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100720bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
721{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100722 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
723
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100724 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
725 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
726
727 if (!input.IsValid() || !alpha.IsValid())
728 {
729 return Fail("%s: Operation has invalid inputs", __func__);
730 }
731
732 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
733
734 if (!output)
735 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100736 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100737 }
738
739 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
740 const armnn::TensorInfo& alphaInfo = alpha.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100741 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100742
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100743 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100744 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100745 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100746 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100747
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100748 bool isSupported = false;
749 FORWARD_LAYER_SUPPORT_FUNC(__func__,
750 IsPreluSupported,
751 data.m_Backends,
752 isSupported,
753 inputInfo,
754 alphaInfo,
755 outputInfo);
756 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100757 {
758 return false;
759 }
760
761 armnn::IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
762
763 if (!layer)
764 {
765 return Fail("%s: AddPreluLayer failed", __func__);
766 }
767
Sadik Armagan64b19b52019-08-19 09:49:58 +0100768 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
769 if (!isReshapeSupported)
770 {
771 return false;
772 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100773
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100774 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100775}
776
Sadik Armagan5a476a82019-07-30 09:43:18 +0100777bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
778{
779 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
780
781 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
782 if (!input.IsValid())
783 {
784 return Fail("%s: Operation has invalid input", __func__);
785 }
786
787 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
788 if (!outputOperand)
789 {
790 return Fail("%s: Operation has invalid outputs", __func__);
791 }
792
793 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
794 if (IsDynamicTensor(outputInfo))
795 {
796 return Fail("%s: Dynamic output tensors are not supported", __func__);
797 }
798
799 bool isSupported = false;
800 FORWARD_LAYER_SUPPORT_FUNC(__func__,
801 IsQuantizeSupported,
802 data.m_Backends,
803 isSupported,
804 input.GetTensorInfo(),
805 outputInfo);
806 if (!isSupported)
807 {
808 return false;
809 }
810
811 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
812 assert(layer != nullptr);
813 input.Connect(layer->GetInputSlot(0));
814
815 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
816}
817
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100818bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
819{
820 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
821
822 //Inputs:
823 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
824 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
825 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
826 if (!input.IsValid())
827 {
828 return Fail("%s: Could not read input 0: input", __func__);
829 }
830
831 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
832 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
833 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
834 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
835 if (!previousCellStateIn.IsValid())
836 {
837 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
838 }
839
840 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
841 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
842 // is quantized with a fixed quantization range of -1, 127/128.
843 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
844 if (!previousOutputIn.IsValid())
845 {
846 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
847 }
848
849 // Get the input tensors:
850 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
851 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
852 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
853 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100854 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100855
856 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
857 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
858 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
859 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100860 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100861
862 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
863 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
864 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
865 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100866 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100867
868 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
869 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
870 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
871 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100872 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100873
874 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
875 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
876 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
877 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100878 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100879
880 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
881 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
882 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
883 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100884 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100885
886 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
887 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
888 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
889 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100890 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100891
892 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
893 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
894 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
895 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100896 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100897
898 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
899 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
900 // of input and weights scales and zeroPoint equal to 0.
901 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100902 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100903
904 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
905 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
906 // of input and weights scales and zeroPoint equal to 0.
907 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100908 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100909
910 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
911 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
912 // and weights scales and zeroPoint equal to 0.
913 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100914 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100915
916 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
917 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
918 // of input and weights scales and zeroPoint equal to 0.
919 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100920 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100921
922 if (!inputToInputWeightsPin.IsValid() ||
923 !inputToForgetWeightsPin.IsValid() ||
924 !inputToCellWeightsPin.IsValid() ||
925 !inputToOutputWeightsPin.IsValid() ||
926 !recurrentToInputWeightsPin.IsValid() ||
927 !recurrentToForgetWeightsPin.IsValid() ||
928 !recurrentToCellWeightsPin.IsValid() ||
929 !recurrentToOutputWeightsPin.IsValid() ||
930 !inputGateBiasPin.IsValid() ||
931 !forgetGateBiasPin.IsValid() ||
932 !cellBiasPin.IsValid() ||
933 !outputGateBiasPin.IsValid())
934 {
935 return Fail("%s: Operation has invalid tensor inputs", __func__);
936 }
937
938 // Outputs:
939 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
940 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
941 // of -2^4, 2^4 * 32767/32768.
942 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
943 if (!cellStateOut)
944 {
945 return Fail("%s: Could not read output 0: cellStateOut", __func__);
946 }
947
948 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
949 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
950 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
951 if (!output)
952 {
953 return Fail("%s: Could not read output 1: output", __func__);
954 }
955
956 // Inputs
957 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
958 const armnn::TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
959 const armnn::TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
960
961 // Outputs
962 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
963 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
964
965 // Dynamic tensors currently not supported
966 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
967 {
968 return Fail("%s: Dynamic output tensors are not supported", __func__);
969 }
970
971 armnn::QuantizedLstmInputParams params;
972
973 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
974 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
975 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
976 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
977 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
978 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
979 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
980 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
981 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
982 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
983 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
984 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
985
986 armnn::QuantizedLstmInputParamsInfo paramsInfo;
987 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
988 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
989 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
990 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
991 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
992 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
993 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
994 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
995 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
996 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
997 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
998 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
999
1000 bool isSupported = false;
1001 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1002 IsQuantizedLstmSupported,
1003 data.m_Backends,
1004 isSupported,
1005 inputInfo,
1006 previousCellStateInInfo,
1007 previousOutputInInfo,
1008 cellStateOutInfo,
1009 outputInfo,
1010 paramsInfo);
1011
1012 if (!isSupported)
1013 {
1014 return false;
1015 }
1016
1017 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1018 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001019 previousCellStateIn.Connect(layer->GetInputSlot(1));
1020 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001021
1022 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1023 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1024}
1025
Sadik Armagan61113162019-07-25 09:09:40 +01001026bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1027{
1028 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1029 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1030}
1031
1032bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1033{
1034 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1035 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1036}
1037
1038bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1039{
1040 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1041 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1042}
1043
Mike Kelly46272802019-08-14 17:00:48 +01001044bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1045{
1046 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1047 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1048}
1049
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001050bool HalPolicy::ConvertResize(const Operation& operation,
1051 const Model& model,
1052 ConversionData& data,
1053 armnn::ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001054{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001055 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
1056
1057 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001058 if (!input.IsValid())
1059 {
1060 return Fail("%s: Could not read input 0", __func__);
1061 }
1062
1063 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1064 if (!output)
1065 {
1066 return Fail("%s: Could not read output 0", __func__);
1067 }
1068
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001069 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1070 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1071
1072 if (IsDynamicTensor(outputInfo))
1073 {
1074 return Fail("%s: Dynamic output tensors are not supported", __func__);
1075 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001076
1077 armnn::ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001078 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001079 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1080
1081 OperandType operandType1;
1082 OperandType operandType2;
1083
1084 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1085 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1086 {
1087 return Fail("%s: Operation has invalid inputs", __func__);
1088 }
1089
1090 if (operandType1 != operandType2)
1091 {
1092 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1093 }
1094
1095 if (operandType1 == OperandType::INT32)
1096 {
1097 // Case 1: resizing by shape
1098 int32_t targetWidth = 0;
1099 int32_t targetHeight = 0;
1100
1101 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1102 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1103 {
1104 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1105 }
1106
1107 if (targetWidth < 0 || targetHeight < 0)
1108 {
1109 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1110 "Target width/height cannot be < 0", __func__);
1111 }
1112
1113 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001114 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001115 }
1116 else if (operandType1 == OperandType::FLOAT32)
1117 {
1118 // Case 2: resizing by scale
1119 float widthScale = 1.0f;
1120 float heightScale = 1.0f;
1121
1122 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1123 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1124 {
1125 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1126 }
1127
1128 const armnn::TensorShape& inputShape = inputInfo.GetShape();
1129 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1130
1131 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1132 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1133
1134 descriptor.m_TargetWidth = std::floor(width * widthScale);
1135 descriptor.m_TargetHeight = std::floor(height * heightScale);
1136 }
1137 else
1138 {
1139 // NOTE: FLOAT16 scales are not supported
1140 return false;
1141 }
1142
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001143 bool isSupported = false;
1144 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1145 IsResizeSupported,
1146 data.m_Backends,
1147 isSupported,
1148 inputInfo,
1149 outputInfo,
1150 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001151
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001152 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001153 {
1154 return false;
1155 }
1156
1157 armnn::IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
1158
1159 assert(layer != nullptr);
1160
1161 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1162 input.Connect(layer->GetInputSlot(0));
1163
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001164 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001165}
1166
Finn Williamsd74c5052019-07-30 17:06:00 +01001167bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1168{
1169 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1170 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1171}
1172
Keith Davisa6bc52f2019-06-26 09:39:49 +01001173bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1174{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001175 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001176
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001177 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001178 if (!input.IsValid() )
1179 {
1180 return Fail("%s: Operation has invalid inputs", __func__);
1181 }
1182
1183 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1184 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001185 if (rank != 4)
1186 {
1187 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1188 }
1189
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001190 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1191 if (!output)
1192 {
1193 return Fail("%s: Could not read output 0", __func__);
1194 }
1195
1196 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1197 if (IsDynamicTensor(outputInfo))
1198 {
1199 return Fail("%s: Dynamic output tensors are not supported", __func__);
1200 }
1201
Keith Davisa6bc52f2019-06-26 09:39:49 +01001202 armnn::SpaceToDepthDescriptor desc;
1203
1204 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1205
1206 if (desc.m_BlockSize <= 1)
1207 {
1208 return Fail("%s: Block size must be at least 1 in all dimensions");
1209 }
1210
1211 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1212
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001213 bool isSupported = false;
1214 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1215 IsSpaceToDepthSupported,
1216 data.m_Backends,
1217 isSupported,
1218 inputInfo,
1219 outputInfo,
1220 desc);
1221 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01001222 {
1223 return false;
1224 }
1225
1226 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
1227 assert(layer != nullptr);
1228 input.Connect(layer->GetInputSlot(0));
1229
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001230 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001231}
1232
Francis Murtagh074c25a2019-07-22 16:40:57 +01001233bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1234{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001235 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1236
Francis Murtagh074c25a2019-07-22 16:40:57 +01001237 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1238 if (!input.IsValid())
1239 {
1240 return Fail("%s: Operation has invalid inputs", __func__);
1241 }
1242
1243 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1244 if (!outputOperand)
1245 {
1246 return Fail("%s: Operation has no outputs", __func__);
1247 }
1248
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001249 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001250 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001251 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001252 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001253 }
1254
1255 armnn::SoftmaxDescriptor desc;
1256 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1257 {
1258 return Fail("%s: Operation has invalid inputs", __func__);
1259 }
1260
1261 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1262 2,
1263 HalPolicy::OperandType::INT32,
1264 desc.m_Axis,
1265 model,
1266 data))
1267 {
1268 return Fail("%s: Operation has invalid inputs", __func__);
1269 }
1270
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01001271 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1272 !(desc.m_Axis == 1 ||
1273 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1274 {
1275 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1276 }
1277
Francis Murtagh074c25a2019-07-22 16:40:57 +01001278 bool isSupported = false;
1279 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1280 IsSoftmaxSupported,
1281 data.m_Backends,
1282 isSupported,
1283 input.GetTensorInfo(),
1284 outputInfo,
1285 desc);
1286 if (!isSupported)
1287 {
1288 return false;
1289 }
1290
1291 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1292 assert(layer != nullptr);
1293 input.Connect(layer->GetInputSlot(0));
1294
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001295 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001296}
1297
Mike Kelly0a879362019-07-29 16:56:31 +01001298bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
1299{
1300 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
1301 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
1302}
1303
Sadik Armagan61113162019-07-25 09:09:40 +01001304bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1305{
1306 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
1307 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
1308}
1309
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001310bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
1311{
1312 // Inputs:
1313 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
1314 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
1315 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1316 if (!input.IsValid())
1317 {
1318 return Fail("%s: Could not read input 0: input", __func__);
1319 }
1320 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1321 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
1322 if (!outputStateIn.IsValid())
1323 {
1324 return Fail("%s: Could not read input 18: outputStateIn", __func__);
1325 }
1326 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1327 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
1328 if (!cellStateIn.IsValid())
1329 {
1330 return Fail("%s: Could not read input 19: cellStateIn", __func__);
1331 }
1332
1333 // Get the mandatory input tensors:
1334 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1335 // [num_units, input_size].
1336 const ConstTensorPin inputToForgetWeightsPin =
1337 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1338 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1339 // [num_units, input_size].
1340 const ConstTensorPin inputToCellWeightsPin =
1341 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
1342 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1343 // [num_units, input_size].
1344 const ConstTensorPin inputToOutputWeightsPin =
1345 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
1346 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1347 // [num_units, output_size].
1348 const ConstTensorPin recurrentToForgetWeightsPin =
1349 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
1350 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1351 // [num_units, output_size].
1352 const ConstTensorPin recurrentToCellWeightsPin =
1353 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
1354 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1355 // [num_units, output_size].
1356 const ConstTensorPin recurrentToOutputWeightsPin =
1357 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
1358 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1359 const ConstTensorPin forgetGateBiasPin =
1360 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
1361 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1362 const ConstTensorPin cellBiasPin =
1363 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
1364 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1365 const ConstTensorPin outputGateBiasPin =
1366 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
1367
1368 if (!inputToForgetWeightsPin.IsValid() ||
1369 !inputToCellWeightsPin.IsValid() ||
1370 !inputToOutputWeightsPin.IsValid() ||
1371 !recurrentToForgetWeightsPin.IsValid() ||
1372 !recurrentToCellWeightsPin.IsValid() ||
1373 !recurrentToOutputWeightsPin.IsValid() ||
1374 !forgetGateBiasPin.IsValid() ||
1375 !cellBiasPin.IsValid() ||
1376 !outputGateBiasPin.IsValid())
1377 {
1378 return Fail("%s: Operation has invalid tensor inputs", __func__);
1379 }
1380
1381 // Get the optional input tensors:
1382 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1383 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
1384 const ConstTensorPin inputToInputWeightsPin =
1385 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1386 1,
1387 model,
1388 data,
1389 g_DontPermute,
1390 nullptr,
1391 true);
1392
1393 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1394 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
1395 // “num_units”), or the second dimension of the “projection_weights”, if defined.
1396 const ConstTensorPin recurrentToInputWeightsPin =
1397 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1398 5,
1399 model,
1400 data,
1401 g_DontPermute,
1402 nullptr,
1403 true);
1404
1405 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1406 const ConstTensorPin cellToInputWeightsPin =
1407 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1408 9,
1409 model,
1410 data,
1411 g_DontPermute,
1412 nullptr,
1413 true);
1414
1415 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1416 const ConstTensorPin cellToForgetWeightsPin =
1417 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1418 10,
1419 model,
1420 data,
1421 g_DontPermute,
1422 nullptr,
1423 true);
1424
1425 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1426 const ConstTensorPin cellToOutputWeightsPin =
1427 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1428 11,
1429 model,
1430 data,
1431 g_DontPermute,
1432 nullptr,
1433 true);
1434
1435 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1436 const ConstTensorPin inputGateBiasPin =
1437 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1438 12,
1439 model,
1440 data,
1441 g_DontPermute,
1442 nullptr,
1443 true);
1444
1445 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1446 // [output_size, num_units].
1447 const ConstTensorPin projectionWeightsPin =
1448 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1449 16,
1450 model,
1451 data,
1452 g_DontPermute,
1453 nullptr,
1454 true);
1455
1456 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
1457 const ConstTensorPin projectionBiasPin =
1458 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1459 17,
1460 model,
1461 data,
1462 g_DontPermute,
1463 nullptr,
1464 true);
1465
1466 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
1467 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
1468 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
1469 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
1470 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
1471 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
1472 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
1473 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
1474 {
1475 return Fail("%s: Operation has invalid tensor inputs", __func__);
1476 }
1477
1478 // Get the mandatory input scalars (actually 1-D tensors of size 1):
1479 // 20: The activation function: A value indicating the activation function:
1480 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
1481 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
1482 // If set to 0.0 then clipping is disabled.
1483 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
1484 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
1485 ActivationFn activation;
1486 float cellClip;
1487 float projClip;
1488 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
1489 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
1490 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
1491 {
1492 return Fail("%s: Operation has invalid scalar inputs", __func__);
1493 }
1494
1495 // Get the normalization tensors
1496 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
1497 // Used to rescale normalized inputs to activation at input gate.
1498 const ConstTensorPin inputLayerNormWeightsPin =
1499 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1500 23,
1501 model,
1502 data,
1503 g_DontPermute,
1504 nullptr,
1505 true);
1506
1507 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
1508 // Used to rescale normalized inputs to activation at forget gate.
1509 const ConstTensorPin forgetLayerNormWeightsPin =
1510 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1511 24,
1512 model,
1513 data,
1514 g_DontPermute,
1515 nullptr,
1516 true);
1517
1518 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
1519 // Used to rescale normalized inputs to activation at cell gate.
1520 const ConstTensorPin cellLayerNormWeightsPin =
1521 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1522 25,
1523 model,
1524 data,
1525 g_DontPermute,
1526 nullptr,
1527 true);
1528
1529 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
1530 // Used to rescale normalized inputs to activation at output gate.
1531 const ConstTensorPin outputLayerNormWeightsPin =
1532 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1533 26,
1534 model,
1535 data,
1536 g_DontPermute,
1537 nullptr,
1538 true);
1539
1540 // Outputs:
1541 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
1542 // with CIFG, or [batch_size, num_units * 3] without CIFG.
1543 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1544 if (!scratchBuffer)
1545 {
1546 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
1547 }
1548 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1549 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1550 if (!outputStateOut)
1551 {
1552 return Fail("%s: Could not read output 1: outputStateOut", __func__);
1553 }
1554 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1555 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
1556 if (!cellStateOut)
1557 {
1558 return Fail("%s: Could not read output 2: cellStateOut", __func__);
1559 }
1560 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
1561 // effectively the same as the current “output state (out)” value.
1562 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
1563 if (!output)
1564 {
1565 return Fail("%s: Could not read output 3: output", __func__);
1566 }
1567
1568 // set the params structure for the AddLstmLayer call
1569 armnn::LstmInputParams params;
1570 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1571 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1572 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1573 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1574 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1575 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1576 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1577 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1578 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
1579 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
1580 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
1581 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1582 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1583 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1584 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1585 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
1586 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
1587 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
1588 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
1589 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
1590 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
1591
1592 // set the layer descriptor
1593 armnn::LstmDescriptor desc;
1594 desc.m_ActivationFunc = activation;
1595 desc.m_ClippingThresCell = cellClip;
1596 desc.m_ClippingThresProj = projClip;
1597 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
1598 params.m_RecurrentToInputWeights == nullptr ||
1599 params.m_InputGateBias == nullptr);
1600 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
1601 params.m_CellToOutputWeights != nullptr);
1602 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
1603 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
1604 params.m_ForgetLayerNormWeights != nullptr ||
1605 params.m_CellLayerNormWeights != nullptr ||
1606 params.m_OutputLayerNormWeights != nullptr);
1607
1608 // validate the optional input groups
1609 if (desc.m_CifgEnabled &&
1610 (params.m_InputToInputWeights != nullptr ||
1611 params.m_RecurrentToInputWeights != nullptr ||
1612 params.m_InputGateBias != nullptr))
1613 {
1614 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
1615 " and input gate bias must be provided", __func__);
1616 }
1617
1618 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
1619 {
1620 return Fail("%s: projection bias should not be provided without projection weights", __func__);
1621 }
1622
1623 if (desc.m_PeepholeEnabled &&
1624 (params.m_CellToForgetWeights == nullptr ||
1625 params.m_CellToOutputWeights == nullptr ||
1626 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
1627 {
1628 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
1629 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
1630 }
1631
1632 if (desc.m_LayerNormEnabled &&
1633 (params.m_ForgetLayerNormWeights == nullptr ||
1634 params.m_CellLayerNormWeights == nullptr ||
1635 params.m_OutputLayerNormWeights == nullptr ||
1636 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
1637 {
1638 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
1639 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
1640 }
1641
1642 // Check if the layer is supported
1643 // Inputs
1644 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1645 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
1646 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
1647
1648 // Outputs
1649 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
1650 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
1651 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1652 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1653
Ferran Balaguera4a629a2019-07-30 10:16:13 +01001654 if (IsDynamicTensor(scratchBufferInfo) ||
1655 IsDynamicTensor(outputStateOutInfo) ||
1656 IsDynamicTensor(cellStateOutInfo) ||
1657 IsDynamicTensor(outputInfo))
1658 {
1659 return Fail("%s: Dynamic output tensors are not supported", __func__);
1660 }
1661
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001662 // Basic parameters
1663 armnn::LstmInputParamsInfo paramsInfo;
1664 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1665 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1666 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1667 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1668 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1669 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1670 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1671 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1672 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1673
1674 // Optional parameters
1675 if(!desc.m_CifgEnabled)
1676 {
1677 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1678 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1679 if (params.m_CellToInputWeights != nullptr)
1680 {
1681 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
1682 }
1683 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1684 }
1685
1686 if(desc.m_ProjectionEnabled)
1687 {
1688 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
1689 if (params.m_ProjectionBias != nullptr)
1690 {
1691 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
1692 }
1693 }
1694
1695 if(desc.m_PeepholeEnabled)
1696 {
1697 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
1698 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
1699 }
1700
1701 if (desc.m_LayerNormEnabled)
1702 {
1703 if(!desc.m_CifgEnabled)
1704 {
1705 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
1706 }
1707 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
1708 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
1709 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
1710 }
1711
1712 bool isSupported = false;
1713 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1714 IsLstmSupported,
1715 data.m_Backends,
1716 isSupported,
1717 inputInfo,
1718 outputStateInInfo,
1719 cellStateInInfo,
1720 scratchBufferInfo,
1721 outputStateOutInfo,
1722 cellStateOutInfo,
1723 outputInfo,
1724 desc,
1725 paramsInfo);
1726 if (!isSupported)
1727 {
1728 return false;
1729 }
1730
1731 // Add the layer
1732 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
1733
1734 input.Connect(layer->GetInputSlot(0));
1735 outputStateIn.Connect(layer->GetInputSlot(1));
1736 cellStateIn.Connect(layer->GetInputSlot(2));
1737
1738 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1739 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
1740 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
1741 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
1742}
1743
Sadik Armagan701d9a02019-09-04 15:16:18 +01001744bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
1745{
1746 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
1747 armnn::ActivationDescriptor desc;
1748 desc.m_Function = armnn::ActivationFunction::Sqrt;
1749
1750 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
1751}
1752
Mike Kelly46272802019-08-14 17:00:48 +01001753bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
1754{
Sadik Armagan701d9a02019-09-04 15:16:18 +01001755 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01001756 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
1757}
1758
1759bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
1760{
Sadik Armagan701d9a02019-09-04 15:16:18 +01001761 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01001762 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
1763}
1764
1765bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
1766{
Sadik Armagan701d9a02019-09-04 15:16:18 +01001767 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01001768 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
1769}
1770
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01001771bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01001772{
1773 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1774
1775 if (!input.IsValid())
1776 {
1777 return Fail("%s: Operation has invalid inputs", __func__);
1778 }
1779
1780 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1781
1782 if (!output)
1783 {
1784 return Fail("%s: Could not read output 0", __func__);
1785 }
1786
1787 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1788 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1789 if (IsDynamicTensor(outputInfo))
1790 {
1791 return Fail("%s: Dynamic output tensors are not supported", __func__);
1792 }
1793
1794 // ArmNN does not currently support non-fixed weights or bias
1795 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
1796 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1797
1798 if (weightsOperand == nullptr)
1799 {
1800 return Fail("%s: Operand is invalid", __func__);
1801 }
1802 armnn::TransposeConvolution2dDescriptor desc;
1803 desc.m_DataLayout = armnn::DataLayout::NHWC;
1804
1805 // Determine whether padding is implicit or explicit
1806 bool implicitPadding = operation.inputs.size() == 9;
1807
1808 if (implicitPadding )
1809 {
1810 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
1811 }
1812 else
1813 {
1814 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
1815 }
1816
1817 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
1818 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1819 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1820
1821 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
1822
1823 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
1824 // We have to permute it to OIHW if the data layout is NCHW.
1825 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
1826 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
1827 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
1828
1829 // Bias is a 1D tensor
1830 const ConstTensorPin biasPin =
1831 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1832
1833 if (!weightsPin.IsValid())
1834 {
1835 return Fail("%s: Operation has invalid weights", __func__);
1836 }
1837
1838 if (!biasPin.IsValid())
1839 {
1840 return Fail("%s: Operation has invalid biases", __func__);
1841 }
1842
1843 armnn::ConstTensor weights = weightsPin.GetConstTensor();
1844 armnn::ConstTensor bias = biasPin.GetConstTensor();
1845 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1846
1847 ActivationFn activation;
1848
1849 if (implicitPadding)
1850 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001851 int32_t strideX{0};
1852 int32_t strideY{0};
1853 int32_t padLeft{0};
1854 int32_t padRight{0};
1855 int32_t padTop{0};
1856 int32_t padBottom{0};
1857
David Monahan613b49c2019-06-27 11:37:47 +01001858 android::nn::PaddingScheme paddingScheme;
1859 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001860 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
1861 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01001862 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
1863 {
1864 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1865 }
1866
1867 const uint32_t kernelX = weights.GetShape()[widthIndex];
1868 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001869 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
1870 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01001871
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001872 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
1873 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
1874
1875 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
1876 // but Arm NN only supports values >= 0
1877 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
1878 {
1879 return Fail("%s: Negative padding values are not supported", __func__);
1880 }
1881
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001882 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
1883 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001884 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
1885 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
1886 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
1887 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01001888 }
1889 else if (operation.inputs.size() == 11)
1890 {
1891 // explicit padding
1892 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
1893 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
1894 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
1895 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
1896 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
1897 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
1898 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
1899 {
1900 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1901 }
1902 }
1903 else
1904 {
1905 return Fail("%s: Unsupported number of operation inputs", __func__);
1906 }
1907
1908 desc.m_BiasEnabled = true;
1909 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1910
1911 bool isSupported = false;
1912 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1913 IsTransposeConvolution2dSupported,
1914 data.m_Backends,
1915 isSupported,
1916 inputInfo,
1917 outputInfo,
1918 desc,
1919 weights.GetInfo(),
1920 biases);
1921 if (!isSupported)
1922 {
1923 return false;
1924 }
1925
1926 armnn::IConnectableLayer* startLayer =
1927 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1928 if (!startLayer)
1929 {
1930 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
1931 }
1932
1933 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1934 if (!endLayer)
1935 {
1936 return Fail("%s: ProcessActivation failed", __func__);
1937 }
1938
1939 input.Connect(startLayer->GetInputSlot(0));
1940
1941 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
1942}
1943
Mike Kellyb5fdf382019-06-11 16:35:25 +01001944} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001945} // namespace armnn_driver