blob: 0a12fd24582eedd218cd01c22e77c1df4c45ed37 [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 Kellyd486e522019-12-17 12:19:09 +000030 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);
Pablo Telloc05798c2019-12-17 10:10:53 +000046 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);
Mike Kelly46272802019-08-14 17:00:48 +010086 case V1_2::OperationType::SQUEEZE:
87 return ConvertSqueeze(operation, model, data);
88 case V1_2::OperationType::STRIDED_SLICE:
89 return ConvertStridedSlice(operation, model, data);
90 case V1_2::OperationType::TRANSPOSE:
91 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +010092 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +010093 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +010094 case V1_2::OperationType::SOFTMAX:
95 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +010096 case V1_2::OperationType::SPACE_TO_BATCH_ND :
97 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +010098 case V1_2::OperationType::SPACE_TO_DEPTH:
99 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100100 case V1_2::OperationType::SUB:
101 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100102 case V1_2::OperationType::TANH:
103 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100104 default:
105 return Fail("%s: Operation type %s not supported in ArmnnDriver",
106 __func__, toString(operation.type).c_str());
107 }
108}
109
Mike Kelly46272802019-08-14 17:00:48 +0100110bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
111{
112 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
113 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
114}
115
Sadik Armagan15d63e22019-07-26 16:59:35 +0100116bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
117{
118 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
119 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Average, model, data);
120}
121
Finn Williams23b87b32019-07-30 11:44:05 +0100122bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
123{
124 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
125 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
126}
127
Mike Kellyd486e522019-12-17 12:19:09 +0000128bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
129{
130 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
131 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
132}
133
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100134bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
135{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100136 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
137
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100138 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
139 if (!input.IsValid())
140 {
141 return Fail("%s: Operation has invalid inputs", __func__);
142 }
143
144 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
145 if (!output)
146 {
147 return Fail("%s: Could not read output 0", __func__);
148 }
149
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100150 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
151 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
152
153 if (IsDynamicTensor(outputInfo))
154 {
155 return Fail("%s: Dynamic output tensors are not supported", __func__);
156 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100157
Mike Kellye1d60bb2019-07-11 11:44:52 +0100158 armnn::Convolution2dDescriptor desc;
159 desc.m_DataLayout = armnn::DataLayout::NHWC;
160
161 // Determine whether padding is implicit or explicit
162 bool implicitPadding = operation.inputs.size() == 7 ||
163 (operation.inputs.size() >= 8 &&
164 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
165
166 if (implicitPadding)
167 {
168 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
169 }
170 else if (operation.inputs.size() >= 10)
171 {
172 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
173 }
174
175 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
176
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100177 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100178 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
179 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
180 // the DataLayout is NCHW
181 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
182 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
183 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100184 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100185 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100186
187 if (!weightsPin.IsValid())
188 {
189 return Fail("%s: Operation has invalid weights", __func__);
190 }
191
192 if (!biasPin.IsValid())
193 {
194 return Fail("%s: Operation has invalid biases", __func__);
195 }
196
197 armnn::ConstTensor weights = weightsPin.GetConstTensor();
198 armnn::ConstTensor bias = biasPin.GetConstTensor();
199 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
200
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100201 ActivationFn activation;
202
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100203 if (implicitPadding)
204 {
205 android::nn::PaddingScheme paddingScheme;
206 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
207 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
208 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
209 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
210 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
211 {
212 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
213 }
214
Mike Kellye1d60bb2019-07-11 11:44:52 +0100215 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
216 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
217 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
218 const uint32_t kernelX = weights.GetShape()[widthIndex];
219 const uint32_t kernelY = weights.GetShape()[heightIndex];
220 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
221 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100222
Mike Kelly86b36d42019-07-12 16:39:33 +0100223 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
224 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100225
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100226 }
227 else if (operation.inputs.size() >= 10)
228 {
229 // explicit padding
230 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
231 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
232 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
233 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
234 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
235 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
236 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
237 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
238 {
239 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
240 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100241 }
242 else
243 {
244 return Fail("%s: Unsupported number of operation inputs", __func__);
245 }
246
247 desc.m_BiasEnabled = true;
248 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
249
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100250 bool isSupported = false;
251 FORWARD_LAYER_SUPPORT_FUNC(__func__,
252 IsConvolution2dSupported,
253 data.m_Backends,
254 isSupported,
255 inputInfo,
256 outputInfo,
257 desc,
258 weights.GetInfo(),
259 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100260
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100261 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100262 {
263 return false;
264 }
265
266 armnn::IConnectableLayer* startLayer =
267 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
268
269 if (!startLayer)
270 {
271 return Fail("%s: AddConvolution2dLayer failed", __func__);
272 }
273
274 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
275
276 if (!endLayer)
277 {
278 return Fail("%s: ProcessActivation failed", __func__);
279 }
280
281 input.Connect(startLayer->GetInputSlot(0));
282
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100283 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100284}
285
286bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
287{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100288 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
289
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100290 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
291
292 if (!input.IsValid())
293 {
294 return Fail("%s: Operation has invalid inputs", __func__);
295 }
296
297 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
298
299 if (!output)
300 {
301 return Fail("%s: Could not read output 0", __func__);
302 }
303
304 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100305 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
306
307 if (IsDynamicTensor(outputInfo))
308 {
309 return Fail("%s: Dynamic output tensors are not supported", __func__);
310 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100311
312 // ArmNN does not currently support non-fixed weights or bias
313 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
314 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
315
316 if (weightsOperand == nullptr)
317 {
318 return Fail("%s: Operand is invalid", __func__);
319 }
Mike Kellyc7d0d442019-12-11 19:27:11 +0000320 if ( weightsOperand->dimensions[0] != 1)
321 {
322 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
323 __func__, weightsOperand->dimensions[0] );
324 }
325
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100326 armnn::DepthwiseConvolution2dDescriptor desc;
327 desc.m_DataLayout = armnn::DataLayout::NHWC;
328
329 // Determine whether padding is implicit or explicit
330 bool implicitPadding = operation.inputs.size() == 8 ||
331 (operation.inputs.size() >= 9 &&
332 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
333
334 // Look ahead to find the optional DataLayout, if present
335 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
336 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
337
338 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
339 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
340 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
341 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
342
343 // Reinterpret weight data as [ H, W, I, M ]
344 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
345 weightsOperand->dimensions[2],
346 inputInfo.GetShape()[channelsIndex],
347 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
348
349 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
350 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
351
352 const ConstTensorPin weightsPin =
353 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
354 1,
355 model,
356 data,
357 HWIMToMIHW,
358 &weightsShape);
359
360 // Bias is a 1D tensor
361 const ConstTensorPin biasPin =
362 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
363
364 if (!weightsPin.IsValid())
365 {
366 return Fail("%s: Operation has invalid weights", __func__);
367 }
368
369 if (!biasPin.IsValid())
370 {
371 return Fail("%s: Operation has invalid biases", __func__);
372 }
373
374 armnn::ConstTensor weights = weightsPin.GetConstTensor();
375 armnn::ConstTensor bias = biasPin.GetConstTensor();
376 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
377
378 ActivationFn activation;
379
380 if (implicitPadding)
381 {
382 android::nn::PaddingScheme paddingScheme;
383 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
384 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
385 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
386 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
387 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
388 {
389 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
390 }
391
392 const uint32_t kernelX = weights.GetShape()[3];
393 const uint32_t kernelY = weights.GetShape()[2];
394 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
395 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
396
Mike Kelly86b36d42019-07-12 16:39:33 +0100397 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
398 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100399 }
400 else if (operation.inputs.size() >= 11)
401 {
402 // explicit padding
403 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
404 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
405 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
406 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
407 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
408 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
409 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
410 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
411 {
412 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
413 }
414 }
415 else
416 {
417 return Fail("%s: Unsupported number of operation inputs", __func__);
418 }
419
420 desc.m_BiasEnabled = true;
421 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
422
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100423 bool isSupported = false;
424 FORWARD_LAYER_SUPPORT_FUNC(__func__,
425 IsDepthwiseConvolutionSupported,
426 data.m_Backends,
427 isSupported,
428 inputInfo,
429 outputInfo,
430 desc,
431 weights.GetInfo(),
432 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100433
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100434 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100435 {
436 return false;
437 }
438
439 armnn::IConnectableLayer* startLayer =
440 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100441
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100442 if (!startLayer)
443 {
444 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
445 }
446
447 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
448 if (!endLayer)
449 {
450 return Fail("%s: ProcessActivation failed", __func__);
451 }
452
453 input.Connect(startLayer->GetInputSlot(0));
454
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100455 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100456}
457
Mike Kelly46272802019-08-14 17:00:48 +0100458bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
459{
460 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
461 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
462}
463
464bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
465{
466 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
467 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
468}
469
470bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
471{
472 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
473 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
474}
475
476bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
477{
478 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
479 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
480}
481
482bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
483{
484 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
485 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
486}
487
Pablo Telloc05798c2019-12-17 10:10:53 +0000488bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
489{
490 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
491 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
492}
493
Mike Kelly46272802019-08-14 17:00:48 +0100494bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
495 const Model& model,
496 ConversionData& data)
497{
498 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
499 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
500}
501
502bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
503{
504 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
505 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
506}
507
Sadik Armagan15d63e22019-07-26 16:59:35 +0100508bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
509{
510 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
511 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
512}
513
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100514bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
515{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100516 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
517
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100518 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
519 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
520
521 if (!input0.IsValid() || !input1.IsValid())
522 {
523 return Fail("%s: Operation has invalid inputs", __func__);
524 }
525
526 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
527 if (!outputOperand)
528 {
529 return Fail("%s: Could not read output", __func__);
530 }
531
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100532 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100533 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100534 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100535 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100536 }
537
Aron Virginas-Tard7593232019-07-16 13:17:06 +0100538 bool isSupported = false;
539 FORWARD_LAYER_SUPPORT_FUNC(__func__,
540 IsMaximumSupported,
541 data.m_Backends,
542 isSupported,
543 input0.GetTensorInfo(),
544 input1.GetTensorInfo(),
545 outInfo);
546
547 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100548 {
549 return false;
550 }
551
552 armnn::IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
553 assert(layer != nullptr);
Derek Lamberti57ea6d12019-12-19 15:45:35 +0000554 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100555 if (!isReshapeSupported)
556 {
557 return false;
558 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100559
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100560 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100561}
562
Mike Kelly46272802019-08-14 17:00:48 +0100563bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
564{
565 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
566 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
567}
568
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100569bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
570{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100571 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
572
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100573 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
574 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
575
576 if (!input0.IsValid() || !input1.IsValid())
577 {
578 return Fail("%s: Operation has invalid inputs", __func__);
579 }
580
581 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
582 if (!output)
583 {
584 return Fail("%s: Could not read output 0", __func__);
585 }
586
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100587 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100588 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100589 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100590 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100591 }
592
593 bool isSupported = false;
594 FORWARD_LAYER_SUPPORT_FUNC(__func__,
595 IsMinimumSupported,
596 data.m_Backends,
597 isSupported,
598 input0.GetTensorInfo(),
599 input1.GetTensorInfo(),
600 outputInfo);
601
602 if (!isSupported)
603 {
604 return false;
605 }
606
607 armnn::IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
608 assert(layer != nullptr);
Derek Lamberti57ea6d12019-12-19 15:45:35 +0000609 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100610 if (!isReshapeSupported)
611 {
612 return false;
613 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100614
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100615 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100616}
617
Mike Kelly46272802019-08-14 17:00:48 +0100618bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
619{
620 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
621 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
622}
623
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100624bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
625{
626 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
627 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
628}
629
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100630bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
631{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100632 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
633
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100634 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
635 if (!input.IsValid())
636 {
637 return Fail("%s: Could not read input 0", __func__);
638 }
639
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100640 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
641 if (!output)
642 {
643 return Fail("%s: Could not read output", __func__);
644 }
645
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100646 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
647 unsigned int rank = inputInfo.GetNumDimensions();
648
649 armnn::PadDescriptor descriptor;
650 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
651 {
652 return Fail("%s: Could not convert paddings", __func__);
653 }
654
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100655 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100656 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100657 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100658 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100659 }
660
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100661 // Determine type of padding value
662 OperandType operandType0;
663 OperandType operandType2;
664
665 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
666 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
667 {
668 return Fail("%s: Operation has invalid inputs", __func__);
669 }
670
671 // Read value to use for padding
672 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
673 {
674 armnn::Half f16PadValue;
675 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
676 {
677 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
678 }
679
680 descriptor.m_PadValue = f16PadValue;
681 }
682 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
683 {
684 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
685 {
686 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
687 }
688 }
689 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
690 {
Mike Kelly3c673942019-07-25 09:26:06 +0100691 int32_t intPadValue = 0;
692 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100693 {
694 return Fail("%s: Could not read input 2 (INT32)", __func__);
695 }
Mike Kelly3c673942019-07-25 09:26:06 +0100696 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100697 }
698 else
699 {
700 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
701 }
702
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100703 bool isSupported = false;
704 FORWARD_LAYER_SUPPORT_FUNC(__func__,
705 IsPadSupported,
706 data.m_Backends,
707 isSupported,
708 inputInfo,
709 outputInfo,
710 descriptor);
711 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100712 {
713 return false;
714 }
715
716 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
717 assert(layer != nullptr);
718 input.Connect(layer->GetInputSlot(0));
719 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
720
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100721 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100722}
723
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100724bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
725{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100726 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
727
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100728 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
729 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
730
731 if (!input.IsValid() || !alpha.IsValid())
732 {
733 return Fail("%s: Operation has invalid inputs", __func__);
734 }
735
736 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
737
738 if (!output)
739 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100740 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100741 }
742
743 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
744 const armnn::TensorInfo& alphaInfo = alpha.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100745 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100746
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100747 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100748 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100749 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100750 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100751
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100752 bool isSupported = false;
753 FORWARD_LAYER_SUPPORT_FUNC(__func__,
754 IsPreluSupported,
755 data.m_Backends,
756 isSupported,
757 inputInfo,
758 alphaInfo,
759 outputInfo);
760 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100761 {
762 return false;
763 }
764
765 armnn::IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
766
767 if (!layer)
768 {
769 return Fail("%s: AddPreluLayer failed", __func__);
770 }
771
Derek Lamberti57ea6d12019-12-19 15:45:35 +0000772 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100773 if (!isReshapeSupported)
774 {
775 return false;
776 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100777
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100778 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100779}
780
Sadik Armagan5a476a82019-07-30 09:43:18 +0100781bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
782{
783 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
784
785 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
786 if (!input.IsValid())
787 {
788 return Fail("%s: Operation has invalid input", __func__);
789 }
790
791 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
792 if (!outputOperand)
793 {
794 return Fail("%s: Operation has invalid outputs", __func__);
795 }
796
797 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
798 if (IsDynamicTensor(outputInfo))
799 {
800 return Fail("%s: Dynamic output tensors are not supported", __func__);
801 }
802
803 bool isSupported = false;
804 FORWARD_LAYER_SUPPORT_FUNC(__func__,
805 IsQuantizeSupported,
806 data.m_Backends,
807 isSupported,
808 input.GetTensorInfo(),
809 outputInfo);
810 if (!isSupported)
811 {
812 return false;
813 }
814
815 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
816 assert(layer != nullptr);
817 input.Connect(layer->GetInputSlot(0));
818
819 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
820}
821
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100822bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
823{
824 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
825
826 //Inputs:
827 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
828 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
829 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
830 if (!input.IsValid())
831 {
832 return Fail("%s: Could not read input 0: input", __func__);
833 }
834
835 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
836 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
837 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
838 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
839 if (!previousCellStateIn.IsValid())
840 {
841 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
842 }
843
844 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
845 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
846 // is quantized with a fixed quantization range of -1, 127/128.
847 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
848 if (!previousOutputIn.IsValid())
849 {
850 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
851 }
852
853 // Get the input tensors:
854 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
855 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
856 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
857 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100858 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100859
860 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
861 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
862 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
863 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100864 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100865
866 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
867 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
868 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
869 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100870 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100871
872 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
873 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
874 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
875 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100876 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100877
878 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
879 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
880 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
881 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100882 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100883
884 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
885 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
886 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
887 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100888 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100889
890 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
891 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
892 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
893 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100894 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100895
896 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
897 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
898 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
899 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100900 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100901
902 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
903 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
904 // of input and weights scales and zeroPoint equal to 0.
905 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100906 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100907
908 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
909 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
910 // of input and weights scales and zeroPoint equal to 0.
911 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100912 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100913
914 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
915 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
916 // and weights scales and zeroPoint equal to 0.
917 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100918 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100919
920 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
921 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
922 // of input and weights scales and zeroPoint equal to 0.
923 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100924 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100925
926 if (!inputToInputWeightsPin.IsValid() ||
927 !inputToForgetWeightsPin.IsValid() ||
928 !inputToCellWeightsPin.IsValid() ||
929 !inputToOutputWeightsPin.IsValid() ||
930 !recurrentToInputWeightsPin.IsValid() ||
931 !recurrentToForgetWeightsPin.IsValid() ||
932 !recurrentToCellWeightsPin.IsValid() ||
933 !recurrentToOutputWeightsPin.IsValid() ||
934 !inputGateBiasPin.IsValid() ||
935 !forgetGateBiasPin.IsValid() ||
936 !cellBiasPin.IsValid() ||
937 !outputGateBiasPin.IsValid())
938 {
939 return Fail("%s: Operation has invalid tensor inputs", __func__);
940 }
941
942 // Outputs:
943 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
944 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
945 // of -2^4, 2^4 * 32767/32768.
946 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
947 if (!cellStateOut)
948 {
949 return Fail("%s: Could not read output 0: cellStateOut", __func__);
950 }
951
952 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
953 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
954 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
955 if (!output)
956 {
957 return Fail("%s: Could not read output 1: output", __func__);
958 }
959
960 // Inputs
961 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
962 const armnn::TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
963 const armnn::TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
964
965 // Outputs
966 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
967 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
968
969 // Dynamic tensors currently not supported
970 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
971 {
972 return Fail("%s: Dynamic output tensors are not supported", __func__);
973 }
974
975 armnn::QuantizedLstmInputParams params;
976
977 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
978 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
979 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
980 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
981 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
982 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
983 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
984 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
985 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
986 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
987 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
988 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
989
990 armnn::QuantizedLstmInputParamsInfo paramsInfo;
991 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
992 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
993 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
994 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
995 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
996 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
997 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
998 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
999 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1000 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1001 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1002 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1003
1004 bool isSupported = false;
1005 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1006 IsQuantizedLstmSupported,
1007 data.m_Backends,
1008 isSupported,
1009 inputInfo,
1010 previousCellStateInInfo,
1011 previousOutputInInfo,
1012 cellStateOutInfo,
1013 outputInfo,
1014 paramsInfo);
1015
1016 if (!isSupported)
1017 {
1018 return false;
1019 }
1020
1021 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1022 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001023 previousCellStateIn.Connect(layer->GetInputSlot(1));
1024 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001025
1026 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1027 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1028}
1029
Sadik Armagan61113162019-07-25 09:09:40 +01001030bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1031{
1032 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1033 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1034}
1035
1036bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1037{
1038 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1039 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1040}
1041
1042bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1043{
1044 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1045 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1046}
1047
Mike Kelly46272802019-08-14 17:00:48 +01001048bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1049{
1050 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1051 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1052}
1053
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001054bool HalPolicy::ConvertResize(const Operation& operation,
1055 const Model& model,
1056 ConversionData& data,
1057 armnn::ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001058{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001059 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
1060
1061 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001062 if (!input.IsValid())
1063 {
1064 return Fail("%s: Could not read input 0", __func__);
1065 }
1066
1067 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1068 if (!output)
1069 {
1070 return Fail("%s: Could not read output 0", __func__);
1071 }
1072
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001073 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1074 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1075
1076 if (IsDynamicTensor(outputInfo))
1077 {
1078 return Fail("%s: Dynamic output tensors are not supported", __func__);
1079 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001080
1081 armnn::ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001082 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001083 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1084
1085 OperandType operandType1;
1086 OperandType operandType2;
1087
1088 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1089 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1090 {
1091 return Fail("%s: Operation has invalid inputs", __func__);
1092 }
1093
1094 if (operandType1 != operandType2)
1095 {
1096 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1097 }
1098
1099 if (operandType1 == OperandType::INT32)
1100 {
1101 // Case 1: resizing by shape
1102 int32_t targetWidth = 0;
1103 int32_t targetHeight = 0;
1104
1105 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1106 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1107 {
1108 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1109 }
1110
1111 if (targetWidth < 0 || targetHeight < 0)
1112 {
1113 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1114 "Target width/height cannot be < 0", __func__);
1115 }
1116
1117 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001118 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001119 }
1120 else if (operandType1 == OperandType::FLOAT32)
1121 {
1122 // Case 2: resizing by scale
1123 float widthScale = 1.0f;
1124 float heightScale = 1.0f;
1125
1126 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1127 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1128 {
1129 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1130 }
1131
1132 const armnn::TensorShape& inputShape = inputInfo.GetShape();
1133 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1134
1135 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1136 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1137
1138 descriptor.m_TargetWidth = std::floor(width * widthScale);
1139 descriptor.m_TargetHeight = std::floor(height * heightScale);
1140 }
1141 else
1142 {
1143 // NOTE: FLOAT16 scales are not supported
1144 return false;
1145 }
1146
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001147 bool isSupported = false;
1148 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1149 IsResizeSupported,
1150 data.m_Backends,
1151 isSupported,
1152 inputInfo,
1153 outputInfo,
1154 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001155
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001156 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001157 {
1158 return false;
1159 }
1160
1161 armnn::IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
1162
1163 assert(layer != nullptr);
1164
1165 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1166 input.Connect(layer->GetInputSlot(0));
1167
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001168 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001169}
1170
Finn Williamsd74c5052019-07-30 17:06:00 +01001171bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1172{
1173 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1174 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1175}
1176
Keith Davisa6bc52f2019-06-26 09:39:49 +01001177bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1178{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001179 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001180
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001181 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001182 if (!input.IsValid() )
1183 {
1184 return Fail("%s: Operation has invalid inputs", __func__);
1185 }
1186
1187 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1188 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001189 if (rank != 4)
1190 {
1191 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1192 }
1193
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001194 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1195 if (!output)
1196 {
1197 return Fail("%s: Could not read output 0", __func__);
1198 }
1199
1200 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1201 if (IsDynamicTensor(outputInfo))
1202 {
1203 return Fail("%s: Dynamic output tensors are not supported", __func__);
1204 }
1205
Keith Davisa6bc52f2019-06-26 09:39:49 +01001206 armnn::SpaceToDepthDescriptor desc;
1207
1208 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1209
1210 if (desc.m_BlockSize <= 1)
1211 {
1212 return Fail("%s: Block size must be at least 1 in all dimensions");
1213 }
1214
1215 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1216
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001217 bool isSupported = false;
1218 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1219 IsSpaceToDepthSupported,
1220 data.m_Backends,
1221 isSupported,
1222 inputInfo,
1223 outputInfo,
1224 desc);
1225 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01001226 {
1227 return false;
1228 }
1229
1230 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
1231 assert(layer != nullptr);
1232 input.Connect(layer->GetInputSlot(0));
1233
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001234 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001235}
1236
Francis Murtagh074c25a2019-07-22 16:40:57 +01001237bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1238{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001239 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1240
Francis Murtagh074c25a2019-07-22 16:40:57 +01001241 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1242 if (!input.IsValid())
1243 {
1244 return Fail("%s: Operation has invalid inputs", __func__);
1245 }
1246
1247 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1248 if (!outputOperand)
1249 {
1250 return Fail("%s: Operation has no outputs", __func__);
1251 }
1252
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001253 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001254 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001255 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001256 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001257 }
1258
1259 armnn::SoftmaxDescriptor desc;
1260 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1261 {
1262 return Fail("%s: Operation has invalid inputs", __func__);
1263 }
1264
1265 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1266 2,
1267 HalPolicy::OperandType::INT32,
1268 desc.m_Axis,
1269 model,
1270 data))
1271 {
1272 return Fail("%s: Operation has invalid inputs", __func__);
1273 }
1274
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01001275 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1276 !(desc.m_Axis == 1 ||
1277 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1278 {
1279 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1280 }
1281
Francis Murtagh074c25a2019-07-22 16:40:57 +01001282 bool isSupported = false;
1283 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1284 IsSoftmaxSupported,
1285 data.m_Backends,
1286 isSupported,
1287 input.GetTensorInfo(),
1288 outputInfo,
1289 desc);
1290 if (!isSupported)
1291 {
1292 return false;
1293 }
1294
1295 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1296 assert(layer != nullptr);
1297 input.Connect(layer->GetInputSlot(0));
1298
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001299 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001300}
1301
Mike Kelly0a879362019-07-29 16:56:31 +01001302bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
1303{
1304 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
1305 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
1306}
1307
Sadik Armagan61113162019-07-25 09:09:40 +01001308bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1309{
1310 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
1311 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
1312}
1313
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001314bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
1315{
1316 // Inputs:
1317 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
1318 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
1319 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1320 if (!input.IsValid())
1321 {
1322 return Fail("%s: Could not read input 0: input", __func__);
1323 }
1324 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1325 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
1326 if (!outputStateIn.IsValid())
1327 {
1328 return Fail("%s: Could not read input 18: outputStateIn", __func__);
1329 }
1330 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1331 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
1332 if (!cellStateIn.IsValid())
1333 {
1334 return Fail("%s: Could not read input 19: cellStateIn", __func__);
1335 }
1336
1337 // Get the mandatory input tensors:
1338 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1339 // [num_units, input_size].
1340 const ConstTensorPin inputToForgetWeightsPin =
1341 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1342 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1343 // [num_units, input_size].
1344 const ConstTensorPin inputToCellWeightsPin =
1345 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
1346 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1347 // [num_units, input_size].
1348 const ConstTensorPin inputToOutputWeightsPin =
1349 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
1350 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1351 // [num_units, output_size].
1352 const ConstTensorPin recurrentToForgetWeightsPin =
1353 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
1354 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1355 // [num_units, output_size].
1356 const ConstTensorPin recurrentToCellWeightsPin =
1357 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
1358 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1359 // [num_units, output_size].
1360 const ConstTensorPin recurrentToOutputWeightsPin =
1361 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
1362 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1363 const ConstTensorPin forgetGateBiasPin =
1364 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
1365 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1366 const ConstTensorPin cellBiasPin =
1367 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
1368 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1369 const ConstTensorPin outputGateBiasPin =
1370 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
1371
1372 if (!inputToForgetWeightsPin.IsValid() ||
1373 !inputToCellWeightsPin.IsValid() ||
1374 !inputToOutputWeightsPin.IsValid() ||
1375 !recurrentToForgetWeightsPin.IsValid() ||
1376 !recurrentToCellWeightsPin.IsValid() ||
1377 !recurrentToOutputWeightsPin.IsValid() ||
1378 !forgetGateBiasPin.IsValid() ||
1379 !cellBiasPin.IsValid() ||
1380 !outputGateBiasPin.IsValid())
1381 {
1382 return Fail("%s: Operation has invalid tensor inputs", __func__);
1383 }
1384
1385 // Get the optional input tensors:
1386 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1387 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
1388 const ConstTensorPin inputToInputWeightsPin =
1389 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1390 1,
1391 model,
1392 data,
1393 g_DontPermute,
1394 nullptr,
1395 true);
1396
1397 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1398 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
1399 // “num_units”), or the second dimension of the “projection_weights”, if defined.
1400 const ConstTensorPin recurrentToInputWeightsPin =
1401 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1402 5,
1403 model,
1404 data,
1405 g_DontPermute,
1406 nullptr,
1407 true);
1408
1409 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1410 const ConstTensorPin cellToInputWeightsPin =
1411 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1412 9,
1413 model,
1414 data,
1415 g_DontPermute,
1416 nullptr,
1417 true);
1418
1419 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1420 const ConstTensorPin cellToForgetWeightsPin =
1421 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1422 10,
1423 model,
1424 data,
1425 g_DontPermute,
1426 nullptr,
1427 true);
1428
1429 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1430 const ConstTensorPin cellToOutputWeightsPin =
1431 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1432 11,
1433 model,
1434 data,
1435 g_DontPermute,
1436 nullptr,
1437 true);
1438
1439 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1440 const ConstTensorPin inputGateBiasPin =
1441 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1442 12,
1443 model,
1444 data,
1445 g_DontPermute,
1446 nullptr,
1447 true);
1448
1449 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1450 // [output_size, num_units].
1451 const ConstTensorPin projectionWeightsPin =
1452 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1453 16,
1454 model,
1455 data,
1456 g_DontPermute,
1457 nullptr,
1458 true);
1459
1460 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
1461 const ConstTensorPin projectionBiasPin =
1462 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1463 17,
1464 model,
1465 data,
1466 g_DontPermute,
1467 nullptr,
1468 true);
1469
1470 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
1471 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
1472 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
1473 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
1474 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
1475 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
1476 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
1477 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
1478 {
1479 return Fail("%s: Operation has invalid tensor inputs", __func__);
1480 }
1481
1482 // Get the mandatory input scalars (actually 1-D tensors of size 1):
1483 // 20: The activation function: A value indicating the activation function:
1484 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
1485 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
1486 // If set to 0.0 then clipping is disabled.
1487 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
1488 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
1489 ActivationFn activation;
1490 float cellClip;
1491 float projClip;
1492 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
1493 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
1494 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
1495 {
1496 return Fail("%s: Operation has invalid scalar inputs", __func__);
1497 }
1498
1499 // Get the normalization tensors
1500 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
1501 // Used to rescale normalized inputs to activation at input gate.
1502 const ConstTensorPin inputLayerNormWeightsPin =
1503 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1504 23,
1505 model,
1506 data,
1507 g_DontPermute,
1508 nullptr,
1509 true);
1510
1511 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
1512 // Used to rescale normalized inputs to activation at forget gate.
1513 const ConstTensorPin forgetLayerNormWeightsPin =
1514 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1515 24,
1516 model,
1517 data,
1518 g_DontPermute,
1519 nullptr,
1520 true);
1521
1522 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
1523 // Used to rescale normalized inputs to activation at cell gate.
1524 const ConstTensorPin cellLayerNormWeightsPin =
1525 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1526 25,
1527 model,
1528 data,
1529 g_DontPermute,
1530 nullptr,
1531 true);
1532
1533 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
1534 // Used to rescale normalized inputs to activation at output gate.
1535 const ConstTensorPin outputLayerNormWeightsPin =
1536 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1537 26,
1538 model,
1539 data,
1540 g_DontPermute,
1541 nullptr,
1542 true);
1543
1544 // Outputs:
1545 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
1546 // with CIFG, or [batch_size, num_units * 3] without CIFG.
1547 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1548 if (!scratchBuffer)
1549 {
1550 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
1551 }
1552 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1553 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1554 if (!outputStateOut)
1555 {
1556 return Fail("%s: Could not read output 1: outputStateOut", __func__);
1557 }
1558 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1559 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
1560 if (!cellStateOut)
1561 {
1562 return Fail("%s: Could not read output 2: cellStateOut", __func__);
1563 }
1564 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
1565 // effectively the same as the current “output state (out)” value.
1566 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
1567 if (!output)
1568 {
1569 return Fail("%s: Could not read output 3: output", __func__);
1570 }
1571
1572 // set the params structure for the AddLstmLayer call
1573 armnn::LstmInputParams params;
1574 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1575 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1576 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1577 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1578 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1579 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1580 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1581 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1582 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
1583 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
1584 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
1585 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1586 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1587 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1588 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1589 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
1590 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
1591 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
1592 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
1593 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
1594 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
1595
1596 // set the layer descriptor
1597 armnn::LstmDescriptor desc;
1598 desc.m_ActivationFunc = activation;
1599 desc.m_ClippingThresCell = cellClip;
1600 desc.m_ClippingThresProj = projClip;
1601 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
1602 params.m_RecurrentToInputWeights == nullptr ||
1603 params.m_InputGateBias == nullptr);
1604 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
1605 params.m_CellToOutputWeights != nullptr);
1606 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
1607 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
1608 params.m_ForgetLayerNormWeights != nullptr ||
1609 params.m_CellLayerNormWeights != nullptr ||
1610 params.m_OutputLayerNormWeights != nullptr);
1611
1612 // validate the optional input groups
1613 if (desc.m_CifgEnabled &&
1614 (params.m_InputToInputWeights != nullptr ||
1615 params.m_RecurrentToInputWeights != nullptr ||
1616 params.m_InputGateBias != nullptr))
1617 {
1618 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
1619 " and input gate bias must be provided", __func__);
1620 }
1621
1622 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
1623 {
1624 return Fail("%s: projection bias should not be provided without projection weights", __func__);
1625 }
1626
1627 if (desc.m_PeepholeEnabled &&
1628 (params.m_CellToForgetWeights == nullptr ||
1629 params.m_CellToOutputWeights == nullptr ||
1630 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
1631 {
1632 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
1633 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
1634 }
1635
1636 if (desc.m_LayerNormEnabled &&
1637 (params.m_ForgetLayerNormWeights == nullptr ||
1638 params.m_CellLayerNormWeights == nullptr ||
1639 params.m_OutputLayerNormWeights == nullptr ||
1640 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
1641 {
1642 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
1643 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
1644 }
1645
1646 // Check if the layer is supported
1647 // Inputs
1648 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1649 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
1650 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
1651
1652 // Outputs
1653 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
1654 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
1655 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1656 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1657
Ferran Balaguera4a629a2019-07-30 10:16:13 +01001658 if (IsDynamicTensor(scratchBufferInfo) ||
1659 IsDynamicTensor(outputStateOutInfo) ||
1660 IsDynamicTensor(cellStateOutInfo) ||
1661 IsDynamicTensor(outputInfo))
1662 {
1663 return Fail("%s: Dynamic output tensors are not supported", __func__);
1664 }
1665
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001666 // Basic parameters
1667 armnn::LstmInputParamsInfo paramsInfo;
1668 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1669 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1670 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1671 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1672 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1673 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1674 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1675 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1676 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1677
1678 // Optional parameters
1679 if(!desc.m_CifgEnabled)
1680 {
1681 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1682 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1683 if (params.m_CellToInputWeights != nullptr)
1684 {
1685 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
1686 }
1687 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1688 }
1689
1690 if(desc.m_ProjectionEnabled)
1691 {
1692 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
1693 if (params.m_ProjectionBias != nullptr)
1694 {
1695 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
1696 }
1697 }
1698
1699 if(desc.m_PeepholeEnabled)
1700 {
1701 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
1702 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
1703 }
1704
1705 if (desc.m_LayerNormEnabled)
1706 {
1707 if(!desc.m_CifgEnabled)
1708 {
1709 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
1710 }
1711 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
1712 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
1713 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
1714 }
1715
1716 bool isSupported = false;
1717 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1718 IsLstmSupported,
1719 data.m_Backends,
1720 isSupported,
1721 inputInfo,
1722 outputStateInInfo,
1723 cellStateInInfo,
1724 scratchBufferInfo,
1725 outputStateOutInfo,
1726 cellStateOutInfo,
1727 outputInfo,
1728 desc,
1729 paramsInfo);
1730 if (!isSupported)
1731 {
1732 return false;
1733 }
1734
1735 // Add the layer
1736 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
1737
1738 input.Connect(layer->GetInputSlot(0));
1739 outputStateIn.Connect(layer->GetInputSlot(1));
1740 cellStateIn.Connect(layer->GetInputSlot(2));
1741
1742 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1743 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
1744 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
1745 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
1746}
1747
Mike Kelly46272802019-08-14 17:00:48 +01001748bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
1749{
1750 ALOGV("hal_1_1::HalPolicy::ConvertSqueeze()");
1751 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
1752}
1753
1754bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
1755{
1756 ALOGV("hal_1_1::HalPolicy::ConvertStridedSlice()");
1757 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
1758}
1759
1760bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
1761{
1762 ALOGV("hal_1_1::HalPolicy::ConvertTranspose()");
1763 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
1764}
1765
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01001766bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01001767{
1768 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1769
1770 if (!input.IsValid())
1771 {
1772 return Fail("%s: Operation has invalid inputs", __func__);
1773 }
1774
1775 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1776
1777 if (!output)
1778 {
1779 return Fail("%s: Could not read output 0", __func__);
1780 }
1781
1782 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1783 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1784 if (IsDynamicTensor(outputInfo))
1785 {
1786 return Fail("%s: Dynamic output tensors are not supported", __func__);
1787 }
1788
1789 // ArmNN does not currently support non-fixed weights or bias
1790 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
1791 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1792
1793 if (weightsOperand == nullptr)
1794 {
1795 return Fail("%s: Operand is invalid", __func__);
1796 }
1797 armnn::TransposeConvolution2dDescriptor desc;
1798 desc.m_DataLayout = armnn::DataLayout::NHWC;
1799
1800 // Determine whether padding is implicit or explicit
1801 bool implicitPadding = operation.inputs.size() == 9;
1802
1803 if (implicitPadding )
1804 {
1805 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
1806 }
1807 else
1808 {
1809 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
1810 }
1811
1812 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
1813 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1814 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1815
1816 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
1817
1818 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
1819 // We have to permute it to OIHW if the data layout is NCHW.
1820 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
1821 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
1822 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
1823
1824 // Bias is a 1D tensor
1825 const ConstTensorPin biasPin =
1826 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1827
1828 if (!weightsPin.IsValid())
1829 {
1830 return Fail("%s: Operation has invalid weights", __func__);
1831 }
1832
1833 if (!biasPin.IsValid())
1834 {
1835 return Fail("%s: Operation has invalid biases", __func__);
1836 }
1837
1838 armnn::ConstTensor weights = weightsPin.GetConstTensor();
1839 armnn::ConstTensor bias = biasPin.GetConstTensor();
1840 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1841
1842 ActivationFn activation;
1843
1844 if (implicitPadding)
1845 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001846 int32_t strideX{0};
1847 int32_t strideY{0};
1848 int32_t padLeft{0};
1849 int32_t padRight{0};
1850 int32_t padTop{0};
1851 int32_t padBottom{0};
1852
David Monahan613b49c2019-06-27 11:37:47 +01001853 android::nn::PaddingScheme paddingScheme;
1854 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001855 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
1856 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01001857 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
1858 {
1859 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1860 }
1861
1862 const uint32_t kernelX = weights.GetShape()[widthIndex];
1863 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001864 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
1865 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01001866
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001867 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
1868 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
1869
1870 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
1871 // but Arm NN only supports values >= 0
1872 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
1873 {
1874 return Fail("%s: Negative padding values are not supported", __func__);
1875 }
1876
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001877 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
1878 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001879 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
1880 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
1881 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
1882 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01001883 }
1884 else if (operation.inputs.size() == 11)
1885 {
1886 // explicit padding
1887 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
1888 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
1889 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
1890 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
1891 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
1892 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
1893 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
1894 {
1895 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1896 }
1897 }
1898 else
1899 {
1900 return Fail("%s: Unsupported number of operation inputs", __func__);
1901 }
1902
1903 desc.m_BiasEnabled = true;
1904 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1905
1906 bool isSupported = false;
1907 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1908 IsTransposeConvolution2dSupported,
1909 data.m_Backends,
1910 isSupported,
1911 inputInfo,
1912 outputInfo,
1913 desc,
1914 weights.GetInfo(),
1915 biases);
1916 if (!isSupported)
1917 {
1918 return false;
1919 }
1920
1921 armnn::IConnectableLayer* startLayer =
1922 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1923 if (!startLayer)
1924 {
1925 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
1926 }
1927
1928 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1929 if (!endLayer)
1930 {
1931 return Fail("%s: ProcessActivation failed", __func__);
1932 }
1933
1934 input.Connect(startLayer->GetInputSlot(0));
1935
1936 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
1937}
1938
Mike Kellyb5fdf382019-06-11 16:35:25 +01001939} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001940} // namespace armnn_driver