blob: e08ae84f291f06a87b81a16c5a6afc303b017766 [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);
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 Kellyb8805202019-07-31 17:25:43 +0100128bool 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 }
320 armnn::DepthwiseConvolution2dDescriptor desc;
321 desc.m_DataLayout = armnn::DataLayout::NHWC;
322
323 // Determine whether padding is implicit or explicit
324 bool implicitPadding = operation.inputs.size() == 8 ||
325 (operation.inputs.size() >= 9 &&
326 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
327
328 // Look ahead to find the optional DataLayout, if present
329 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
330 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
331
332 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
333 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
334 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
335 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
336
337 // Reinterpret weight data as [ H, W, I, M ]
338 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
339 weightsOperand->dimensions[2],
340 inputInfo.GetShape()[channelsIndex],
341 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
342
343 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
344 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
345
346 const ConstTensorPin weightsPin =
347 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
348 1,
349 model,
350 data,
351 HWIMToMIHW,
352 &weightsShape);
353
354 // Bias is a 1D tensor
355 const ConstTensorPin biasPin =
356 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
357
358 if (!weightsPin.IsValid())
359 {
360 return Fail("%s: Operation has invalid weights", __func__);
361 }
362
363 if (!biasPin.IsValid())
364 {
365 return Fail("%s: Operation has invalid biases", __func__);
366 }
367
368 armnn::ConstTensor weights = weightsPin.GetConstTensor();
369 armnn::ConstTensor bias = biasPin.GetConstTensor();
370 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
371
372 ActivationFn activation;
373
374 if (implicitPadding)
375 {
376 android::nn::PaddingScheme paddingScheme;
377 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
378 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
379 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
380 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
381 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
382 {
383 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
384 }
385
386 const uint32_t kernelX = weights.GetShape()[3];
387 const uint32_t kernelY = weights.GetShape()[2];
388 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
389 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
390
Mike Kelly86b36d42019-07-12 16:39:33 +0100391 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
392 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100393 }
394 else if (operation.inputs.size() >= 11)
395 {
396 // explicit padding
397 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
398 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
399 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
400 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
401 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
402 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
403 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
404 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
405 {
406 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
407 }
408 }
409 else
410 {
411 return Fail("%s: Unsupported number of operation inputs", __func__);
412 }
413
414 desc.m_BiasEnabled = true;
415 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
416
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100417 bool isSupported = false;
418 FORWARD_LAYER_SUPPORT_FUNC(__func__,
419 IsDepthwiseConvolutionSupported,
420 data.m_Backends,
421 isSupported,
422 inputInfo,
423 outputInfo,
424 desc,
425 weights.GetInfo(),
426 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100427
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100428 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100429 {
430 return false;
431 }
432
433 armnn::IConnectableLayer* startLayer =
434 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100435
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100436 if (!startLayer)
437 {
438 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
439 }
440
441 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
442 if (!endLayer)
443 {
444 return Fail("%s: ProcessActivation failed", __func__);
445 }
446
447 input.Connect(startLayer->GetInputSlot(0));
448
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100449 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100450}
451
Mike Kelly46272802019-08-14 17:00:48 +0100452bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
453{
454 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
455 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
456}
457
458bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
459{
460 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
461 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
462}
463
464bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
465{
466 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
467 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
468}
469
470bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
471{
472 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
473 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
474}
475
476bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
477{
478 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
479 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
480}
481
Sadik Armagan15d63e22019-07-26 16:59:35 +0100482bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
483{
484 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
485 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
486}
487
Mike Kelly46272802019-08-14 17:00:48 +0100488bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
489 const Model& model,
490 ConversionData& data)
491{
492 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
493 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
494}
495
496bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
497{
498 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
499 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
500}
501
Sadik Armagan15d63e22019-07-26 16:59:35 +0100502bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
503{
504 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
505 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
506}
507
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100508bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
509{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100510 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
511
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100512 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
513 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
514
515 if (!input0.IsValid() || !input1.IsValid())
516 {
517 return Fail("%s: Operation has invalid inputs", __func__);
518 }
519
520 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
521 if (!outputOperand)
522 {
523 return Fail("%s: Could not read output", __func__);
524 }
525
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100526 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100527 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100528 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100529 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100530 }
531
Aron Virginas-Tard7593232019-07-16 13:17:06 +0100532 bool isSupported = false;
533 FORWARD_LAYER_SUPPORT_FUNC(__func__,
534 IsMaximumSupported,
535 data.m_Backends,
536 isSupported,
537 input0.GetTensorInfo(),
538 input1.GetTensorInfo(),
539 outInfo);
540
541 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100542 {
543 return false;
544 }
545
546 armnn::IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
547 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100548 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
549 if (!isReshapeSupported)
550 {
551 return false;
552 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100553
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100554 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100555}
556
Mike Kelly46272802019-08-14 17:00:48 +0100557bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
558{
559 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
560 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
561}
562
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100563bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
564{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100565 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
566
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100567 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
568 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
569
570 if (!input0.IsValid() || !input1.IsValid())
571 {
572 return Fail("%s: Operation has invalid inputs", __func__);
573 }
574
575 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
576 if (!output)
577 {
578 return Fail("%s: Could not read output 0", __func__);
579 }
580
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100581 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100582 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100583 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100584 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100585 }
586
587 bool isSupported = false;
588 FORWARD_LAYER_SUPPORT_FUNC(__func__,
589 IsMinimumSupported,
590 data.m_Backends,
591 isSupported,
592 input0.GetTensorInfo(),
593 input1.GetTensorInfo(),
594 outputInfo);
595
596 if (!isSupported)
597 {
598 return false;
599 }
600
601 armnn::IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
602 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100603 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
604 if (!isReshapeSupported)
605 {
606 return false;
607 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100608
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100609 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100610}
611
Mike Kelly46272802019-08-14 17:00:48 +0100612bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
613{
614 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
615 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
616}
617
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100618bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
619{
620 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
621 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
622}
623
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100624bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
625{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100626 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
627
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100628 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
629 if (!input.IsValid())
630 {
631 return Fail("%s: Could not read input 0", __func__);
632 }
633
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100634 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
635 if (!output)
636 {
637 return Fail("%s: Could not read output", __func__);
638 }
639
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100640 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
641 unsigned int rank = inputInfo.GetNumDimensions();
642
643 armnn::PadDescriptor descriptor;
644 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
645 {
646 return Fail("%s: Could not convert paddings", __func__);
647 }
648
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100649 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100650 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100651 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100652 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100653 }
654
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100655 // Determine type of padding value
656 OperandType operandType0;
657 OperandType operandType2;
658
659 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
660 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
661 {
662 return Fail("%s: Operation has invalid inputs", __func__);
663 }
664
665 // Read value to use for padding
666 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
667 {
668 armnn::Half f16PadValue;
669 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
670 {
671 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
672 }
673
674 descriptor.m_PadValue = f16PadValue;
675 }
676 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
677 {
678 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
679 {
680 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
681 }
682 }
683 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
684 {
Mike Kelly3c673942019-07-25 09:26:06 +0100685 int32_t intPadValue = 0;
686 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100687 {
688 return Fail("%s: Could not read input 2 (INT32)", __func__);
689 }
Mike Kelly3c673942019-07-25 09:26:06 +0100690 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100691 }
692 else
693 {
694 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
695 }
696
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100697 bool isSupported = false;
698 FORWARD_LAYER_SUPPORT_FUNC(__func__,
699 IsPadSupported,
700 data.m_Backends,
701 isSupported,
702 inputInfo,
703 outputInfo,
704 descriptor);
705 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100706 {
707 return false;
708 }
709
710 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
711 assert(layer != nullptr);
712 input.Connect(layer->GetInputSlot(0));
713 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
714
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100715 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100716}
717
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100718bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
719{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100720 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
721
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100722 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
723 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
724
725 if (!input.IsValid() || !alpha.IsValid())
726 {
727 return Fail("%s: Operation has invalid inputs", __func__);
728 }
729
730 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
731
732 if (!output)
733 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100734 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100735 }
736
737 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
738 const armnn::TensorInfo& alphaInfo = alpha.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100739 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100740
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100741 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100742 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100743 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100744 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100745
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100746 bool isSupported = false;
747 FORWARD_LAYER_SUPPORT_FUNC(__func__,
748 IsPreluSupported,
749 data.m_Backends,
750 isSupported,
751 inputInfo,
752 alphaInfo,
753 outputInfo);
754 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100755 {
756 return false;
757 }
758
759 armnn::IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
760
761 if (!layer)
762 {
763 return Fail("%s: AddPreluLayer failed", __func__);
764 }
765
Sadik Armagan64b19b52019-08-19 09:49:58 +0100766 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
767 if (!isReshapeSupported)
768 {
769 return false;
770 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100771
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100772 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100773}
774
Sadik Armagan5a476a82019-07-30 09:43:18 +0100775bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
776{
777 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
778
779 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
780 if (!input.IsValid())
781 {
782 return Fail("%s: Operation has invalid input", __func__);
783 }
784
785 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
786 if (!outputOperand)
787 {
788 return Fail("%s: Operation has invalid outputs", __func__);
789 }
790
791 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
792 if (IsDynamicTensor(outputInfo))
793 {
794 return Fail("%s: Dynamic output tensors are not supported", __func__);
795 }
796
797 bool isSupported = false;
798 FORWARD_LAYER_SUPPORT_FUNC(__func__,
799 IsQuantizeSupported,
800 data.m_Backends,
801 isSupported,
802 input.GetTensorInfo(),
803 outputInfo);
804 if (!isSupported)
805 {
806 return false;
807 }
808
809 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
810 assert(layer != nullptr);
811 input.Connect(layer->GetInputSlot(0));
812
813 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
814}
815
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100816bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
817{
818 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
819
820 //Inputs:
821 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
822 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
823 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
824 if (!input.IsValid())
825 {
826 return Fail("%s: Could not read input 0: input", __func__);
827 }
828
829 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
830 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
831 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
832 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
833 if (!previousCellStateIn.IsValid())
834 {
835 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
836 }
837
838 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
839 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
840 // is quantized with a fixed quantization range of -1, 127/128.
841 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
842 if (!previousOutputIn.IsValid())
843 {
844 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
845 }
846
847 // Get the input tensors:
848 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
849 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
850 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
851 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100852 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100853
854 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
855 // [outputSize, inputSize] specifying input-to-forget 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 inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100858 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100859
860 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
861 // [outputSize, inputSize] specifying input-to-cell 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 inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100864 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100865
866 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
867 // [outputSize, inputSize] specifying input-to-output 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 inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100870 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100871
872 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
873 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
874 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
875 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100876 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100877
878 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
879 // [outputSize, outputSize] specifying recurrent-to-forget 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 recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100882 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100883
884 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
885 // [outputSize, outputSize] specifying recurrent-to-cell 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 recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100888 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100889
890 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
891 // [outputSize, outputSize] specifying recurrent-to-output 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 recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100894 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100895
896 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
897 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
898 // of input and weights scales and zeroPoint equal to 0.
899 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100900 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100901
902 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
903 // the 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 forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100906 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100907
908 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
909 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
910 // and weights scales and zeroPoint equal to 0.
911 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100912 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100913
914 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
915 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
916 // of input and weights scales and zeroPoint equal to 0.
917 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +0100918 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100919
920 if (!inputToInputWeightsPin.IsValid() ||
921 !inputToForgetWeightsPin.IsValid() ||
922 !inputToCellWeightsPin.IsValid() ||
923 !inputToOutputWeightsPin.IsValid() ||
924 !recurrentToInputWeightsPin.IsValid() ||
925 !recurrentToForgetWeightsPin.IsValid() ||
926 !recurrentToCellWeightsPin.IsValid() ||
927 !recurrentToOutputWeightsPin.IsValid() ||
928 !inputGateBiasPin.IsValid() ||
929 !forgetGateBiasPin.IsValid() ||
930 !cellBiasPin.IsValid() ||
931 !outputGateBiasPin.IsValid())
932 {
933 return Fail("%s: Operation has invalid tensor inputs", __func__);
934 }
935
936 // Outputs:
937 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
938 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
939 // of -2^4, 2^4 * 32767/32768.
940 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
941 if (!cellStateOut)
942 {
943 return Fail("%s: Could not read output 0: cellStateOut", __func__);
944 }
945
946 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
947 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
948 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
949 if (!output)
950 {
951 return Fail("%s: Could not read output 1: output", __func__);
952 }
953
954 // Inputs
955 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
956 const armnn::TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
957 const armnn::TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
958
959 // Outputs
960 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
961 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
962
963 // Dynamic tensors currently not supported
964 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
965 {
966 return Fail("%s: Dynamic output tensors are not supported", __func__);
967 }
968
969 armnn::QuantizedLstmInputParams params;
970
971 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
972 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
973 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
974 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
975 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
976 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
977 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
978 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
979 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
980 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
981 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
982 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
983
984 armnn::QuantizedLstmInputParamsInfo paramsInfo;
985 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
986 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
987 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
988 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
989 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
990 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
991 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
992 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
993 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
994 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
995 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
996 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
997
998 bool isSupported = false;
999 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1000 IsQuantizedLstmSupported,
1001 data.m_Backends,
1002 isSupported,
1003 inputInfo,
1004 previousCellStateInInfo,
1005 previousOutputInInfo,
1006 cellStateOutInfo,
1007 outputInfo,
1008 paramsInfo);
1009
1010 if (!isSupported)
1011 {
1012 return false;
1013 }
1014
1015 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
1016 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001017 previousCellStateIn.Connect(layer->GetInputSlot(1));
1018 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001019
1020 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1021 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1022}
1023
Sadik Armagan61113162019-07-25 09:09:40 +01001024bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1025{
1026 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1027 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1028}
1029
1030bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1031{
1032 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1033 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1034}
1035
1036bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1037{
1038 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1039 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1040}
1041
Mike Kelly46272802019-08-14 17:00:48 +01001042bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1043{
1044 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1045 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1046}
1047
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001048bool HalPolicy::ConvertResize(const Operation& operation,
1049 const Model& model,
1050 ConversionData& data,
1051 armnn::ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001052{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001053 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
1054
1055 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001056 if (!input.IsValid())
1057 {
1058 return Fail("%s: Could not read input 0", __func__);
1059 }
1060
1061 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1062 if (!output)
1063 {
1064 return Fail("%s: Could not read output 0", __func__);
1065 }
1066
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001067 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1068 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1069
1070 if (IsDynamicTensor(outputInfo))
1071 {
1072 return Fail("%s: Dynamic output tensors are not supported", __func__);
1073 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001074
1075 armnn::ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001076 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001077 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1078
1079 OperandType operandType1;
1080 OperandType operandType2;
1081
1082 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1083 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1084 {
1085 return Fail("%s: Operation has invalid inputs", __func__);
1086 }
1087
1088 if (operandType1 != operandType2)
1089 {
1090 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1091 }
1092
1093 if (operandType1 == OperandType::INT32)
1094 {
1095 // Case 1: resizing by shape
1096 int32_t targetWidth = 0;
1097 int32_t targetHeight = 0;
1098
1099 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1100 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1101 {
1102 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1103 }
1104
1105 if (targetWidth < 0 || targetHeight < 0)
1106 {
1107 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1108 "Target width/height cannot be < 0", __func__);
1109 }
1110
1111 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001112 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001113 }
1114 else if (operandType1 == OperandType::FLOAT32)
1115 {
1116 // Case 2: resizing by scale
1117 float widthScale = 1.0f;
1118 float heightScale = 1.0f;
1119
1120 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1121 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1122 {
1123 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1124 }
1125
1126 const armnn::TensorShape& inputShape = inputInfo.GetShape();
1127 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1128
1129 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1130 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1131
1132 descriptor.m_TargetWidth = std::floor(width * widthScale);
1133 descriptor.m_TargetHeight = std::floor(height * heightScale);
1134 }
1135 else
1136 {
1137 // NOTE: FLOAT16 scales are not supported
1138 return false;
1139 }
1140
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001141 bool isSupported = false;
1142 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1143 IsResizeSupported,
1144 data.m_Backends,
1145 isSupported,
1146 inputInfo,
1147 outputInfo,
1148 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001149
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001150 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001151 {
1152 return false;
1153 }
1154
1155 armnn::IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
1156
1157 assert(layer != nullptr);
1158
1159 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1160 input.Connect(layer->GetInputSlot(0));
1161
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001162 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001163}
1164
Finn Williamsd74c5052019-07-30 17:06:00 +01001165bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1166{
1167 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1168 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1169}
1170
Keith Davisa6bc52f2019-06-26 09:39:49 +01001171bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1172{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001173 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001174
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001175 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001176 if (!input.IsValid() )
1177 {
1178 return Fail("%s: Operation has invalid inputs", __func__);
1179 }
1180
1181 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1182 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001183 if (rank != 4)
1184 {
1185 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1186 }
1187
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001188 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1189 if (!output)
1190 {
1191 return Fail("%s: Could not read output 0", __func__);
1192 }
1193
1194 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1195 if (IsDynamicTensor(outputInfo))
1196 {
1197 return Fail("%s: Dynamic output tensors are not supported", __func__);
1198 }
1199
Keith Davisa6bc52f2019-06-26 09:39:49 +01001200 armnn::SpaceToDepthDescriptor desc;
1201
1202 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1203
1204 if (desc.m_BlockSize <= 1)
1205 {
1206 return Fail("%s: Block size must be at least 1 in all dimensions");
1207 }
1208
1209 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1210
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001211 bool isSupported = false;
1212 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1213 IsSpaceToDepthSupported,
1214 data.m_Backends,
1215 isSupported,
1216 inputInfo,
1217 outputInfo,
1218 desc);
1219 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01001220 {
1221 return false;
1222 }
1223
1224 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
1225 assert(layer != nullptr);
1226 input.Connect(layer->GetInputSlot(0));
1227
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001228 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001229}
1230
Francis Murtagh074c25a2019-07-22 16:40:57 +01001231bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1232{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001233 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1234
Francis Murtagh074c25a2019-07-22 16:40:57 +01001235 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1236 if (!input.IsValid())
1237 {
1238 return Fail("%s: Operation has invalid inputs", __func__);
1239 }
1240
1241 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1242 if (!outputOperand)
1243 {
1244 return Fail("%s: Operation has no outputs", __func__);
1245 }
1246
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001247 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001248 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001249 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001250 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001251 }
1252
1253 armnn::SoftmaxDescriptor desc;
1254 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1255 {
1256 return Fail("%s: Operation has invalid inputs", __func__);
1257 }
1258
1259 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1260 2,
1261 HalPolicy::OperandType::INT32,
1262 desc.m_Axis,
1263 model,
1264 data))
1265 {
1266 return Fail("%s: Operation has invalid inputs", __func__);
1267 }
1268
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01001269 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1270 !(desc.m_Axis == 1 ||
1271 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1272 {
1273 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1274 }
1275
Francis Murtagh074c25a2019-07-22 16:40:57 +01001276 bool isSupported = false;
1277 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1278 IsSoftmaxSupported,
1279 data.m_Backends,
1280 isSupported,
1281 input.GetTensorInfo(),
1282 outputInfo,
1283 desc);
1284 if (!isSupported)
1285 {
1286 return false;
1287 }
1288
1289 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1290 assert(layer != nullptr);
1291 input.Connect(layer->GetInputSlot(0));
1292
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001293 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001294}
1295
Mike Kelly0a879362019-07-29 16:56:31 +01001296bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
1297{
1298 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
1299 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
1300}
1301
Sadik Armagan61113162019-07-25 09:09:40 +01001302bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1303{
1304 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
1305 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
1306}
1307
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001308bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
1309{
1310 // Inputs:
1311 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
1312 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
1313 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1314 if (!input.IsValid())
1315 {
1316 return Fail("%s: Could not read input 0: input", __func__);
1317 }
1318 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1319 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
1320 if (!outputStateIn.IsValid())
1321 {
1322 return Fail("%s: Could not read input 18: outputStateIn", __func__);
1323 }
1324 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1325 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
1326 if (!cellStateIn.IsValid())
1327 {
1328 return Fail("%s: Could not read input 19: cellStateIn", __func__);
1329 }
1330
1331 // Get the mandatory input tensors:
1332 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1333 // [num_units, input_size].
1334 const ConstTensorPin inputToForgetWeightsPin =
1335 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1336 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1337 // [num_units, input_size].
1338 const ConstTensorPin inputToCellWeightsPin =
1339 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
1340 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1341 // [num_units, input_size].
1342 const ConstTensorPin inputToOutputWeightsPin =
1343 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
1344 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1345 // [num_units, output_size].
1346 const ConstTensorPin recurrentToForgetWeightsPin =
1347 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
1348 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1349 // [num_units, output_size].
1350 const ConstTensorPin recurrentToCellWeightsPin =
1351 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
1352 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1353 // [num_units, output_size].
1354 const ConstTensorPin recurrentToOutputWeightsPin =
1355 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
1356 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1357 const ConstTensorPin forgetGateBiasPin =
1358 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
1359 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1360 const ConstTensorPin cellBiasPin =
1361 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
1362 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1363 const ConstTensorPin outputGateBiasPin =
1364 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
1365
1366 if (!inputToForgetWeightsPin.IsValid() ||
1367 !inputToCellWeightsPin.IsValid() ||
1368 !inputToOutputWeightsPin.IsValid() ||
1369 !recurrentToForgetWeightsPin.IsValid() ||
1370 !recurrentToCellWeightsPin.IsValid() ||
1371 !recurrentToOutputWeightsPin.IsValid() ||
1372 !forgetGateBiasPin.IsValid() ||
1373 !cellBiasPin.IsValid() ||
1374 !outputGateBiasPin.IsValid())
1375 {
1376 return Fail("%s: Operation has invalid tensor inputs", __func__);
1377 }
1378
1379 // Get the optional input tensors:
1380 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1381 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
1382 const ConstTensorPin inputToInputWeightsPin =
1383 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1384 1,
1385 model,
1386 data,
1387 g_DontPermute,
1388 nullptr,
1389 true);
1390
1391 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1392 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
1393 // “num_units”), or the second dimension of the “projection_weights”, if defined.
1394 const ConstTensorPin recurrentToInputWeightsPin =
1395 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1396 5,
1397 model,
1398 data,
1399 g_DontPermute,
1400 nullptr,
1401 true);
1402
1403 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1404 const ConstTensorPin cellToInputWeightsPin =
1405 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1406 9,
1407 model,
1408 data,
1409 g_DontPermute,
1410 nullptr,
1411 true);
1412
1413 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1414 const ConstTensorPin cellToForgetWeightsPin =
1415 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1416 10,
1417 model,
1418 data,
1419 g_DontPermute,
1420 nullptr,
1421 true);
1422
1423 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1424 const ConstTensorPin cellToOutputWeightsPin =
1425 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1426 11,
1427 model,
1428 data,
1429 g_DontPermute,
1430 nullptr,
1431 true);
1432
1433 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1434 const ConstTensorPin inputGateBiasPin =
1435 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1436 12,
1437 model,
1438 data,
1439 g_DontPermute,
1440 nullptr,
1441 true);
1442
1443 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1444 // [output_size, num_units].
1445 const ConstTensorPin projectionWeightsPin =
1446 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1447 16,
1448 model,
1449 data,
1450 g_DontPermute,
1451 nullptr,
1452 true);
1453
1454 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
1455 const ConstTensorPin projectionBiasPin =
1456 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1457 17,
1458 model,
1459 data,
1460 g_DontPermute,
1461 nullptr,
1462 true);
1463
1464 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
1465 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
1466 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
1467 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
1468 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
1469 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
1470 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
1471 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
1472 {
1473 return Fail("%s: Operation has invalid tensor inputs", __func__);
1474 }
1475
1476 // Get the mandatory input scalars (actually 1-D tensors of size 1):
1477 // 20: The activation function: A value indicating the activation function:
1478 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
1479 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
1480 // If set to 0.0 then clipping is disabled.
1481 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
1482 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
1483 ActivationFn activation;
1484 float cellClip;
1485 float projClip;
1486 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
1487 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
1488 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
1489 {
1490 return Fail("%s: Operation has invalid scalar inputs", __func__);
1491 }
1492
1493 // Get the normalization tensors
1494 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
1495 // Used to rescale normalized inputs to activation at input gate.
1496 const ConstTensorPin inputLayerNormWeightsPin =
1497 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1498 23,
1499 model,
1500 data,
1501 g_DontPermute,
1502 nullptr,
1503 true);
1504
1505 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
1506 // Used to rescale normalized inputs to activation at forget gate.
1507 const ConstTensorPin forgetLayerNormWeightsPin =
1508 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1509 24,
1510 model,
1511 data,
1512 g_DontPermute,
1513 nullptr,
1514 true);
1515
1516 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
1517 // Used to rescale normalized inputs to activation at cell gate.
1518 const ConstTensorPin cellLayerNormWeightsPin =
1519 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1520 25,
1521 model,
1522 data,
1523 g_DontPermute,
1524 nullptr,
1525 true);
1526
1527 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
1528 // Used to rescale normalized inputs to activation at output gate.
1529 const ConstTensorPin outputLayerNormWeightsPin =
1530 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1531 26,
1532 model,
1533 data,
1534 g_DontPermute,
1535 nullptr,
1536 true);
1537
1538 // Outputs:
1539 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
1540 // with CIFG, or [batch_size, num_units * 3] without CIFG.
1541 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1542 if (!scratchBuffer)
1543 {
1544 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
1545 }
1546 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1547 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1548 if (!outputStateOut)
1549 {
1550 return Fail("%s: Could not read output 1: outputStateOut", __func__);
1551 }
1552 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1553 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
1554 if (!cellStateOut)
1555 {
1556 return Fail("%s: Could not read output 2: cellStateOut", __func__);
1557 }
1558 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
1559 // effectively the same as the current “output state (out)” value.
1560 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
1561 if (!output)
1562 {
1563 return Fail("%s: Could not read output 3: output", __func__);
1564 }
1565
1566 // set the params structure for the AddLstmLayer call
1567 armnn::LstmInputParams params;
1568 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1569 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1570 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1571 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1572 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1573 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1574 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1575 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1576 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
1577 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
1578 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
1579 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1580 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1581 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1582 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1583 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
1584 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
1585 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
1586 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
1587 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
1588 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
1589
1590 // set the layer descriptor
1591 armnn::LstmDescriptor desc;
1592 desc.m_ActivationFunc = activation;
1593 desc.m_ClippingThresCell = cellClip;
1594 desc.m_ClippingThresProj = projClip;
1595 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
1596 params.m_RecurrentToInputWeights == nullptr ||
1597 params.m_InputGateBias == nullptr);
1598 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
1599 params.m_CellToOutputWeights != nullptr);
1600 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
1601 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
1602 params.m_ForgetLayerNormWeights != nullptr ||
1603 params.m_CellLayerNormWeights != nullptr ||
1604 params.m_OutputLayerNormWeights != nullptr);
1605
1606 // validate the optional input groups
1607 if (desc.m_CifgEnabled &&
1608 (params.m_InputToInputWeights != nullptr ||
1609 params.m_RecurrentToInputWeights != nullptr ||
1610 params.m_InputGateBias != nullptr))
1611 {
1612 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
1613 " and input gate bias must be provided", __func__);
1614 }
1615
1616 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
1617 {
1618 return Fail("%s: projection bias should not be provided without projection weights", __func__);
1619 }
1620
1621 if (desc.m_PeepholeEnabled &&
1622 (params.m_CellToForgetWeights == nullptr ||
1623 params.m_CellToOutputWeights == nullptr ||
1624 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
1625 {
1626 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
1627 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
1628 }
1629
1630 if (desc.m_LayerNormEnabled &&
1631 (params.m_ForgetLayerNormWeights == nullptr ||
1632 params.m_CellLayerNormWeights == nullptr ||
1633 params.m_OutputLayerNormWeights == nullptr ||
1634 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
1635 {
1636 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
1637 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
1638 }
1639
1640 // Check if the layer is supported
1641 // Inputs
1642 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1643 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
1644 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
1645
1646 // Outputs
1647 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
1648 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
1649 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1650 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1651
Ferran Balaguera4a629a2019-07-30 10:16:13 +01001652 if (IsDynamicTensor(scratchBufferInfo) ||
1653 IsDynamicTensor(outputStateOutInfo) ||
1654 IsDynamicTensor(cellStateOutInfo) ||
1655 IsDynamicTensor(outputInfo))
1656 {
1657 return Fail("%s: Dynamic output tensors are not supported", __func__);
1658 }
1659
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001660 // Basic parameters
1661 armnn::LstmInputParamsInfo paramsInfo;
1662 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1663 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1664 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1665 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1666 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1667 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1668 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1669 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1670 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1671
1672 // Optional parameters
1673 if(!desc.m_CifgEnabled)
1674 {
1675 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1676 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1677 if (params.m_CellToInputWeights != nullptr)
1678 {
1679 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
1680 }
1681 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1682 }
1683
1684 if(desc.m_ProjectionEnabled)
1685 {
1686 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
1687 if (params.m_ProjectionBias != nullptr)
1688 {
1689 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
1690 }
1691 }
1692
1693 if(desc.m_PeepholeEnabled)
1694 {
1695 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
1696 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
1697 }
1698
1699 if (desc.m_LayerNormEnabled)
1700 {
1701 if(!desc.m_CifgEnabled)
1702 {
1703 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
1704 }
1705 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
1706 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
1707 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
1708 }
1709
1710 bool isSupported = false;
1711 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1712 IsLstmSupported,
1713 data.m_Backends,
1714 isSupported,
1715 inputInfo,
1716 outputStateInInfo,
1717 cellStateInInfo,
1718 scratchBufferInfo,
1719 outputStateOutInfo,
1720 cellStateOutInfo,
1721 outputInfo,
1722 desc,
1723 paramsInfo);
1724 if (!isSupported)
1725 {
1726 return false;
1727 }
1728
1729 // Add the layer
1730 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
1731
1732 input.Connect(layer->GetInputSlot(0));
1733 outputStateIn.Connect(layer->GetInputSlot(1));
1734 cellStateIn.Connect(layer->GetInputSlot(2));
1735
1736 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1737 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
1738 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
1739 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
1740}
1741
Mike Kelly46272802019-08-14 17:00:48 +01001742bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
1743{
1744 ALOGV("hal_1_1::HalPolicy::ConvertSqueeze()");
1745 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
1746}
1747
1748bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
1749{
1750 ALOGV("hal_1_1::HalPolicy::ConvertStridedSlice()");
1751 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
1752}
1753
1754bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
1755{
1756 ALOGV("hal_1_1::HalPolicy::ConvertTranspose()");
1757 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
1758}
1759
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01001760bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01001761{
1762 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1763
1764 if (!input.IsValid())
1765 {
1766 return Fail("%s: Operation has invalid inputs", __func__);
1767 }
1768
1769 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1770
1771 if (!output)
1772 {
1773 return Fail("%s: Could not read output 0", __func__);
1774 }
1775
1776 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1777 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1778 if (IsDynamicTensor(outputInfo))
1779 {
1780 return Fail("%s: Dynamic output tensors are not supported", __func__);
1781 }
1782
1783 // ArmNN does not currently support non-fixed weights or bias
1784 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
1785 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1786
1787 if (weightsOperand == nullptr)
1788 {
1789 return Fail("%s: Operand is invalid", __func__);
1790 }
1791 armnn::TransposeConvolution2dDescriptor desc;
1792 desc.m_DataLayout = armnn::DataLayout::NHWC;
1793
1794 // Determine whether padding is implicit or explicit
1795 bool implicitPadding = operation.inputs.size() == 9;
1796
1797 if (implicitPadding )
1798 {
1799 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
1800 }
1801 else
1802 {
1803 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
1804 }
1805
1806 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
1807 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1808 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1809
1810 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
1811
1812 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
1813 // We have to permute it to OIHW if the data layout is NCHW.
1814 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
1815 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
1816 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
1817
1818 // Bias is a 1D tensor
1819 const ConstTensorPin biasPin =
1820 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1821
1822 if (!weightsPin.IsValid())
1823 {
1824 return Fail("%s: Operation has invalid weights", __func__);
1825 }
1826
1827 if (!biasPin.IsValid())
1828 {
1829 return Fail("%s: Operation has invalid biases", __func__);
1830 }
1831
1832 armnn::ConstTensor weights = weightsPin.GetConstTensor();
1833 armnn::ConstTensor bias = biasPin.GetConstTensor();
1834 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1835
1836 ActivationFn activation;
1837
1838 if (implicitPadding)
1839 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001840 int32_t strideX{0};
1841 int32_t strideY{0};
1842 int32_t padLeft{0};
1843 int32_t padRight{0};
1844 int32_t padTop{0};
1845 int32_t padBottom{0};
1846
David Monahan613b49c2019-06-27 11:37:47 +01001847 android::nn::PaddingScheme paddingScheme;
1848 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001849 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
1850 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01001851 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
1852 {
1853 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1854 }
1855
1856 const uint32_t kernelX = weights.GetShape()[widthIndex];
1857 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001858 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
1859 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01001860
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001861 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
1862 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
1863
1864 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
1865 // but Arm NN only supports values >= 0
1866 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
1867 {
1868 return Fail("%s: Negative padding values are not supported", __func__);
1869 }
1870
Sadik Armagan3e3003e2019-08-13 12:54:34 +01001871 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
1872 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01001873 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
1874 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
1875 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
1876 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01001877 }
1878 else if (operation.inputs.size() == 11)
1879 {
1880 // explicit padding
1881 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
1882 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
1883 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
1884 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
1885 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
1886 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
1887 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
1888 {
1889 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1890 }
1891 }
1892 else
1893 {
1894 return Fail("%s: Unsupported number of operation inputs", __func__);
1895 }
1896
1897 desc.m_BiasEnabled = true;
1898 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1899
1900 bool isSupported = false;
1901 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1902 IsTransposeConvolution2dSupported,
1903 data.m_Backends,
1904 isSupported,
1905 inputInfo,
1906 outputInfo,
1907 desc,
1908 weights.GetInfo(),
1909 biases);
1910 if (!isSupported)
1911 {
1912 return false;
1913 }
1914
1915 armnn::IConnectableLayer* startLayer =
1916 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1917 if (!startLayer)
1918 {
1919 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
1920 }
1921
1922 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1923 if (!endLayer)
1924 {
1925 return Fail("%s: ProcessActivation failed", __func__);
1926 }
1927
1928 input.Connect(startLayer->GetInputSlot(0));
1929
1930 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
1931}
1932
Mike Kellyb5fdf382019-06-11 16:35:25 +01001933} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001934} // namespace armnn_driver