blob: cd3d2a6c811c12ba421b6fa9f428b6c951694f28 [file] [log] [blame]
Mike Kellyb5fdf382019-06-11 16:35:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "HalPolicy.hpp"
7
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01008#include "Utils.hpp"
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01009
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010010#include <DataLayoutIndexed.hpp>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +010011#include <Half.hpp>
Narumol Prangnawarat85f96542019-09-12 16:26:29 +010012#include <TensorUtils.hpp>
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010013
Teresa Charlin8f6429d2019-10-01 13:10:15 +010014#include <armnn/TypesUtils.hpp>
15
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010016#include <cmath>
17
Mike Kellyb5fdf382019-06-11 16:35:25 +010018namespace armnn_driver
19{
20namespace hal_1_2
21{
22
Teresa Charlin8f6429d2019-10-01 13:10:15 +010023using namespace armnn;
24
Mike Kellyb5fdf382019-06-11 16:35:25 +010025bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
26{
Mike Kellyb5fdf382019-06-11 16:35:25 +010027 switch (operation.type)
28 {
Kevin May407718f2019-09-09 14:46:41 +010029 case V1_2::OperationType::ABS:
30 return ConvertAbs(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010031 case V1_2::OperationType::ADD:
32 return ConvertAdd(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010033 case V1_2::OperationType::AVERAGE_POOL_2D:
34 return ConvertAveragePool2d(operation, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +010035 case V1_2::OperationType::BATCH_TO_SPACE_ND:
36 return ConvertBatchToSpaceNd(operation, model, data);
Mike Kellyb8805202019-07-31 17:25:43 +010037 case V1_2::OperationType::CONCATENATION:
38 return ConvertConcatenation(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010039 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010040 return ConvertConv2d(operation, model, data);
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +010041 case V1_2::OperationType::DEPTH_TO_SPACE:
42 return ConvertDepthToSpace(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010043 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010044 return ConvertDepthwiseConv2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010045 case V1_2::OperationType::DEQUANTIZE:
46 return ConvertDequantize(operation, model, data);
47 case V1_2::OperationType::DIV:
48 return ConvertDiv(operation, model, data);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +010049 case V1_2::OperationType::EXPAND_DIMS:
50 return ConvertExpandDims(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010051 case V1_2::OperationType::FLOOR:
52 return ConvertFloor(operation, model, data);
53 case V1_2::OperationType::FULLY_CONNECTED:
54 return ConvertFullyConnected(operation, model, data);
Teresa Charlin8f6429d2019-10-01 13:10:15 +010055 case V1_2::OperationType::GROUPED_CONV_2D:
56 return ConvertGroupedConv2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010057 case V1_2::OperationType::L2_NORMALIZATION:
58 return ConvertL2Normalization(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010059 case V1_2::OperationType::L2_POOL_2D:
60 return ConvertL2Pool2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010061 case V1_2::OperationType::LOCAL_RESPONSE_NORMALIZATION:
62 return ConvertLocalResponseNormalization(operation, model, data);
63 case V1_2::OperationType::LOGISTIC:
64 return ConvertLogistic(operation, model, data);
65 case V1_2::OperationType::LSTM:
66 return ConvertLstm(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010067 case V1_2::OperationType::MAX_POOL_2D:
68 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +010069 case V1_2::OperationType::MAXIMUM:
70 return ConvertMaximum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010071 case V1_2::OperationType::MEAN:
72 return ConvertMean(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +010073 case V1_2::OperationType::MINIMUM:
74 return ConvertMinimum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010075 case V1_2::OperationType::MUL:
76 return ConvertMul(operation, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +010077 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +010078 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +010079 case V1_2::OperationType::PAD_V2:
80 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +010081 case V1_2::OperationType::PRELU:
82 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +010083 case V1_2::OperationType::QUANTIZE:
84 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +010085 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
86 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +010087 case V1_2::OperationType::RELU:
88 return ConvertReLu(operation, model, data);
89 case V1_2::OperationType::RELU1:
90 return ConvertReLu1(operation, model, data);
91 case V1_2::OperationType::RELU6:
92 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010093 case V1_2::OperationType::RESHAPE:
94 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +010095 case V1_2::OperationType::RESIZE_BILINEAR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +010096 return ConvertResize(operation, model, data, ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010097 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +010098 return ConvertResize(operation, model, data, ResizeMethod::NearestNeighbor);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +010099 case V1_2::OperationType::RSQRT:
100 return ConvertRsqrt(operation, model, data);
Sadik Armagan701d9a02019-09-04 15:16:18 +0100101 case V1_2::OperationType::SQRT:
102 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100103 case V1_2::OperationType::SQUEEZE:
104 return ConvertSqueeze(operation, model, data);
105 case V1_2::OperationType::STRIDED_SLICE:
106 return ConvertStridedSlice(operation, model, data);
107 case V1_2::OperationType::TRANSPOSE:
108 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +0100109 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +0100110 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +0100111 case V1_2::OperationType::SOFTMAX:
112 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +0100113 case V1_2::OperationType::SPACE_TO_BATCH_ND :
114 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100115 case V1_2::OperationType::SPACE_TO_DEPTH:
116 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100117 case V1_2::OperationType::SUB:
118 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100119 case V1_2::OperationType::TANH:
120 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100121 default:
122 return Fail("%s: Operation type %s not supported in ArmnnDriver",
123 __func__, toString(operation.type).c_str());
124 }
125}
126
Kevin May407718f2019-09-09 14:46:41 +0100127bool HalPolicy::ConvertAbs(const Operation& operation, const Model& model, ConversionData& data)
128{
129 ALOGV("hal_1_2::HalPolicy::ConvertAbs()");
130 return ::ConvertAbs<hal_1_2::HalPolicy>(operation, model, data);
131}
132
Mike Kelly46272802019-08-14 17:00:48 +0100133bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
134{
135 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
136 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
137}
138
Sadik Armagan15d63e22019-07-26 16:59:35 +0100139bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
140{
141 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100142 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Average, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100143}
144
Finn Williams23b87b32019-07-30 11:44:05 +0100145bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
146{
147 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
148 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
149}
150
Mike Kellyb8805202019-07-31 17:25:43 +0100151bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
152{
153 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
154 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
155}
156
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100157bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
158{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100159 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
160
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100161 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
162 if (!input.IsValid())
163 {
164 return Fail("%s: Operation has invalid inputs", __func__);
165 }
166
167 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
168 if (!output)
169 {
170 return Fail("%s: Could not read output 0", __func__);
171 }
172
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100173 const TensorInfo& inputInfo = input.GetTensorInfo();
174 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100175
176 if (IsDynamicTensor(outputInfo))
177 {
178 return Fail("%s: Dynamic output tensors are not supported", __func__);
179 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100180
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100181 Convolution2dDescriptor desc;
182 desc.m_DataLayout = DataLayout::NHWC;
Mike Kellye1d60bb2019-07-11 11:44:52 +0100183
184 // Determine whether padding is implicit or explicit
185 bool implicitPadding = operation.inputs.size() == 7 ||
186 (operation.inputs.size() >= 8 &&
187 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
188
189 if (implicitPadding)
190 {
191 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
192 }
193 else if (operation.inputs.size() >= 10)
194 {
195 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
196 }
197
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100198 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
Mike Kellye1d60bb2019-07-11 11:44:52 +0100199
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100200 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100201 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
202 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
203 // the DataLayout is NCHW
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100204 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
Mike Kellye1d60bb2019-07-11 11:44:52 +0100205 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
206 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100207 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100208 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100209
210 if (!weightsPin.IsValid())
211 {
212 return Fail("%s: Operation has invalid weights", __func__);
213 }
214
215 if (!biasPin.IsValid())
216 {
217 return Fail("%s: Operation has invalid biases", __func__);
218 }
219
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100220 ConstTensor weights = weightsPin.GetConstTensor();
221 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100222 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
223
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100224 ActivationFn activation;
225
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100226 if (implicitPadding)
227 {
228 android::nn::PaddingScheme paddingScheme;
229 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
230 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
231 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
232 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
233 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
234 {
235 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
236 }
237
Mike Kellye1d60bb2019-07-11 11:44:52 +0100238 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
239 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
240 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
241 const uint32_t kernelX = weights.GetShape()[widthIndex];
242 const uint32_t kernelY = weights.GetShape()[heightIndex];
243 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
244 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100245
Mike Kelly86b36d42019-07-12 16:39:33 +0100246 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
247 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100248
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100249 }
250 else if (operation.inputs.size() >= 10)
251 {
252 // explicit padding
253 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
254 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
255 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
256 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
257 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
258 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
259 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
260 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
261 {
262 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
263 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100264 }
265 else
266 {
267 return Fail("%s: Unsupported number of operation inputs", __func__);
268 }
269
270 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100271 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100272
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100273 bool isSupported = false;
274 FORWARD_LAYER_SUPPORT_FUNC(__func__,
275 IsConvolution2dSupported,
276 data.m_Backends,
277 isSupported,
278 inputInfo,
279 outputInfo,
280 desc,
281 weights.GetInfo(),
282 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100283
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100284 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100285 {
286 return false;
287 }
288
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100289 IConnectableLayer* startLayer =
290 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100291
292 if (!startLayer)
293 {
294 return Fail("%s: AddConvolution2dLayer failed", __func__);
295 }
296
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100297 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100298
299 if (!endLayer)
300 {
301 return Fail("%s: ProcessActivation failed", __func__);
302 }
303
304 input.Connect(startLayer->GetInputSlot(0));
305
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100306 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100307}
308
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100309bool HalPolicy::ConvertDepthToSpace(const Operation& operation, const Model& model, ConversionData& data)
310{
311 ALOGV("hal_1_2::HalPolicy::ConvertDepthToSpace()");
312 return ::ConvertDepthToSpace<hal_1_2::HalPolicy>(operation, model, data);
313}
314
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100315bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
316{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100317 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
318
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100319 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
320
321 if (!input.IsValid())
322 {
323 return Fail("%s: Operation has invalid inputs", __func__);
324 }
325
326 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
327
328 if (!output)
329 {
330 return Fail("%s: Could not read output 0", __func__);
331 }
332
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100333 const TensorInfo& inputInfo = input.GetTensorInfo();
334 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100335
336 if (IsDynamicTensor(outputInfo))
337 {
338 return Fail("%s: Dynamic output tensors are not supported", __func__);
339 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100340
341 // ArmNN does not currently support non-fixed weights or bias
342 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
343 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
344
345 if (weightsOperand == nullptr)
346 {
347 return Fail("%s: Operand is invalid", __func__);
348 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100349 DepthwiseConvolution2dDescriptor desc;
350 desc.m_DataLayout = DataLayout::NHWC;
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100351
352 // Determine whether padding is implicit or explicit
353 bool implicitPadding = operation.inputs.size() == 8 ||
354 (operation.inputs.size() >= 9 &&
355 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
356
357 // Look ahead to find the optional DataLayout, if present
358 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
359 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
360
361 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
362 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
363 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
364 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
365
366 // Reinterpret weight data as [ H, W, I, M ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100367 TensorShape weightsShape({ weightsOperand->dimensions[1],
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100368 weightsOperand->dimensions[2],
369 inputInfo.GetShape()[channelsIndex],
370 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
371
372 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100373 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100374
375 const ConstTensorPin weightsPin =
376 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
377 1,
378 model,
379 data,
380 HWIMToMIHW,
381 &weightsShape);
382
383 // Bias is a 1D tensor
384 const ConstTensorPin biasPin =
385 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
386
387 if (!weightsPin.IsValid())
388 {
389 return Fail("%s: Operation has invalid weights", __func__);
390 }
391
392 if (!biasPin.IsValid())
393 {
394 return Fail("%s: Operation has invalid biases", __func__);
395 }
396
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100397 ConstTensor weights = weightsPin.GetConstTensor();
398 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100399 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
400
401 ActivationFn activation;
402
403 if (implicitPadding)
404 {
405 android::nn::PaddingScheme paddingScheme;
406 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
407 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
408 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
409 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
410 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
411 {
412 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
413 }
414
415 const uint32_t kernelX = weights.GetShape()[3];
416 const uint32_t kernelY = weights.GetShape()[2];
417 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
418 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
419
Mike Kelly86b36d42019-07-12 16:39:33 +0100420 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
421 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100422 }
423 else if (operation.inputs.size() >= 11)
424 {
425 // explicit padding
426 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
427 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
428 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
429 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
430 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
431 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
432 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
433 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
434 {
435 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
436 }
437 }
438 else
439 {
440 return Fail("%s: Unsupported number of operation inputs", __func__);
441 }
442
443 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100444 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100445
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100446 bool isSupported = false;
447 FORWARD_LAYER_SUPPORT_FUNC(__func__,
448 IsDepthwiseConvolutionSupported,
449 data.m_Backends,
450 isSupported,
451 inputInfo,
452 outputInfo,
453 desc,
454 weights.GetInfo(),
455 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100456
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100457 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100458 {
459 return false;
460 }
461
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100462 IConnectableLayer* startLayer =
463 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100464
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100465 if (!startLayer)
466 {
467 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
468 }
469
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100470 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100471 if (!endLayer)
472 {
473 return Fail("%s: ProcessActivation failed", __func__);
474 }
475
476 input.Connect(startLayer->GetInputSlot(0));
477
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100478 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100479}
480
Mike Kelly46272802019-08-14 17:00:48 +0100481bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
482{
483 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
484 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
485}
486
487bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
488{
489 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
490 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
491}
492
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100493bool HalPolicy::ConvertExpandDims(const Operation& operation, const Model& model, ConversionData& data)
494{
495 ALOGV("hal_1_2::HalPolicy::ConvertExpandDims()");
496
497 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
498
499 if (!input.IsValid())
500 {
501 return Fail("%s: Operation has invalid input", __func__);
502 }
503
504 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
505 if (!output)
506 {
507 return Fail("%s: Operation has invalid output", __func__);
508 }
509
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100510 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100511 if (IsDynamicTensor(outputInfo))
512 {
513 return Fail("%s: Dynamic output tensors are not supported", __func__);
514 }
515
516 int32_t axis;
517 if (!GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, axis, model, data))
518 {
519 return Fail("%s: failed to get axis input value", __func__);
520 }
521
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100522 TensorShape targetShape;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100523
524 try
525 {
526 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
527 }
528 catch (const std::exception &e)
529 {
530 return Fail("%s: %s", __func__, e.what());
531 }
532
533 if (targetShape != outputInfo.GetShape())
534 {
535 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
536 }
537
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100538 ReshapeDescriptor reshapeDescriptor;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100539 reshapeDescriptor.m_TargetShape = targetShape;
540
541 bool isSupported = false;
542 FORWARD_LAYER_SUPPORT_FUNC(__func__,
543 IsReshapeSupported,
544 data.m_Backends,
545 isSupported,
546 input.GetTensorInfo(),
547 reshapeDescriptor);
548
549 if (!isSupported)
550 {
551 return false;
552 }
553
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100554 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100555 assert(layer != nullptr);
556 input.Connect(layer->GetInputSlot(0));
557
558 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
559}
560
Mike Kelly46272802019-08-14 17:00:48 +0100561bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
562{
563 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
564 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
565}
566
567bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
568{
569 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
570 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
571}
572
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100573bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
574{
575 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
576
577 //
578 // Parse data
579 //
580 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
581 if (!input.IsValid())
582 {
583 return Fail("%s: Operation has invalid inputs", __func__);
584 }
585 const TensorInfo& inputInfo = input.GetTensorInfo();
586
587 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
588 if (!output)
589 {
590 return Fail("%s: Could not read output 0", __func__);
591 }
592 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
593 if (IsDynamicTensor(outputInfo))
594 {
595 return Fail("%s: Dynamic output tensors are not supported", __func__);
596 }
597
598 // Look ahead to determine data layout
599 DataLayout dataLayout = DataLayout::NHWC;
600 if (operation.inputs.size() == 12)
601 {
602 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
603 }
604 else
605 {
606 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
607 }
608
609 // NOTE:
610 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
611 // but Arm NN expects the filter's height and width indices to match the input's height and
612 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
613 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
614 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
615 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
616 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
617 const ConstTensorPin biasesPin =
618 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
619 if (!weightsPin.IsValid() || !biasesPin.IsValid())
620 {
621 return Fail("%s: Operation has invalid inputs", __func__);
622 }
623
624 ConstTensor weights = weightsPin.GetConstTensor();
625 ConstTensor biases = biasesPin.GetConstTensor();
626 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
627
628 const TensorShape& inputShape = inputInfo.GetShape();
629 const TensorShape& outputShape = outputInfo.GetShape();
630 const TensorShape& weightsShape = weights.GetShape();
631 const TensorShape& biasesShape = biases.GetShape();
632
633 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
634 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
635 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
636 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
637
638 Convolution2dDescriptor desc;
639 desc.m_DataLayout = dataLayout;
640 desc.m_BiasEnabled = true;
641
642 int numGroups;
643 ActivationFn activation;
644
645 if (operation.inputs.size() == 12)
646 {
647 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
648 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
649 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
650 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
651 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
652 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
653 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
654 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
655 {
656 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
657 }
658
659 }
660 else if (operation.inputs.size() == 9)
661 {
662 android::nn::PaddingScheme paddingScheme;
663 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
664 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
665 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
666 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
667 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
668 {
669 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
670 }
671
672 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
673 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
674
675 const uint32_t kernelX = weightsShape[widthIndex];
676 const uint32_t kernelY = weightsShape[heightIndex];
677
678 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
679 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
680 }
681 else
682 {
683 return Fail("%s: Unsupported number of operation inputs", __func__);
684 }
685
686 const unsigned int outputChannels = outputShape[channelsIndex];
687
688 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
689 const unsigned int channelMultiplier = outputChannels / numGroups;
690
691 //
692 // Validate all relevant inputs
693 //
694 if (numGroups <= 0)
695 {
696 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
697 }
698
699 if (outputChannels % numGroups != 0u)
700 {
701 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
702 }
703
704 //
705 // Set up Splitter layer
706 //
707 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
708 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
709
710 TensorInfo splitterOutputInfo(4,
711 splitterDimSizes,
712 inputInfo.GetDataType(),
713 inputInfo.GetQuantizationScale(),
714 inputInfo.GetQuantizationOffset());
715
716 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
717
718 ViewsDescriptor splitterDesc(numGroups);
719 for (unsigned int group = 0u; group < numGroups; ++group)
720 {
721 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
722 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
723 {
724 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
725 }
726 }
727
728 bool isSupported = false;
729 FORWARD_LAYER_SUPPORT_FUNC(__func__,
730 IsSplitterSupported,
731 data.m_Backends,
732 isSupported,
733 inputInfo,
734 splitterOutputInfos,
735 splitterDesc);
736 if (!isSupported)
737 {
738 return false;
739 }
740
741 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
742 if (!splitterLayer)
743 {
744 return Fail("%s: Failed to add SplitterLayer", __func__);
745 }
746
747 input.Connect(splitterLayer->GetInputSlot(0));
748 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
749 {
750 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
751 }
752
753 //
754 // Set up Convolution2d layers for each group
755 //
756 TensorShape groupInputShape(inputShape);
757 groupInputShape[channelsIndex] = channelsPerGroup;
758
759 TensorShape groupOutputShape(outputShape);
760 groupOutputShape[channelsIndex] = 1;
761
762 TensorShape groupWeightsShape(weightsShape);
763 groupWeightsShape[0] /= channelMultiplier * numGroups;
764
765 TensorShape groupBiasesShape({ 1 });
766
767 const TensorInfo groupInputInfo (groupInputShape,
768 inputInfo.GetDataType(),
769 inputInfo.GetQuantizationScale(),
770 inputInfo.GetQuantizationOffset());
771 const TensorInfo groupWeightsInfo(groupWeightsShape,
772 weights.GetInfo().GetDataType(),
773 weights.GetInfo().GetQuantizationScale(),
774 weights.GetInfo().GetQuantizationOffset());
775 const TensorInfo groupBiasesInfo (groupBiasesShape,
776 biases.GetInfo().GetDataType(),
777 biases.GetInfo().GetQuantizationScale(),
778 biases.GetInfo().GetQuantizationOffset());
779 const TensorInfo groupOutputInfo (groupOutputShape,
780 outputInfo.GetDataType(),
781 outputInfo.GetQuantizationScale(),
782 outputInfo.GetQuantizationOffset());
783
784 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
785 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
786
787 std::vector<IConnectableLayer*> convLayers(numGroups*channelMultiplier, nullptr);
788 for (unsigned int group = 0u; group < numGroups; ++group)
789 {
790 for (unsigned int m = 0u; m < channelMultiplier; ++m)
791 {
792 auto index = group * channelMultiplier + m;
793
794 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
795 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
796
797 // Extract weights and biases data for current group convolution
798 ConstTensor groupWeights(groupWeightsInfo,
799 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
800 weightsDataOffset));
801 ConstTensor groupBiases(groupBiasesInfo,
802 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
803 biasesDataOffset));
804
805 isSupported = false;
806 FORWARD_LAYER_SUPPORT_FUNC(__func__,
807 IsConvolution2dSupported,
808 data.m_Backends,
809 isSupported,
810 groupInputInfo,
811 groupOutputInfo,
812 desc,
813 groupWeightsInfo,
814 Optional<TensorInfo>(groupBiasesInfo));
815 if (!isSupported)
816 {
817 return false;
818 }
819
820 IConnectableLayer *convLayer =
821 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
822 if (!convLayer)
823 {
824 return Fail("%s: AddConvolution2dLayer failed", __func__);
825 }
826
827 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
828 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
829
830 convLayers[index] = convLayer;
831 }
832 }
833
834 //
835 // Set up Concat layer
836 //
837 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
838 for (unsigned int group = 0u; group < numGroups; ++group)
839 {
840 for (unsigned int m = 0u; m < channelMultiplier; ++m)
841 {
842 auto index = group * channelMultiplier + m;
843 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
844 concatDescriptor.SetConcatAxis(channelsIndex);
845 }
846 }
847
848 isSupported = false;
849 FORWARD_LAYER_SUPPORT_FUNC(__func__,
850 IsConcatSupported,
851 data.m_Backends,
852 isSupported,
853 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
854 outputInfo,
855 concatDescriptor);
856 if (!isSupported)
857 {
858 return false;
859 }
860
861 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
862 if (!concatLayer)
863 {
864 return Fail("%s: AddConcatLayer failed", __func__);
865 }
866
867 for (unsigned int group = 0u; group < numGroups; ++group)
868 {
869 for (unsigned int m = 0u; m < channelMultiplier; ++m)
870 {
871 auto index = group * channelMultiplier + m;
872 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
873 }
874 }
875 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
876
877 //
878 // Set up Activation layer (if it is set)
879 //
880 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
881 if (!endLayer)
882 {
883 return Fail("%s: ProcessActivation failed", __func__);
884 }
885
886 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
887}
888
Mike Kelly46272802019-08-14 17:00:48 +0100889bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
890{
891 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
892 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
893}
894
Sadik Armagan15d63e22019-07-26 16:59:35 +0100895bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
896{
897 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100898 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100899}
900
Mike Kelly46272802019-08-14 17:00:48 +0100901bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
902 const Model& model,
903 ConversionData& data)
904{
905 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
906 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
907}
908
909bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
910{
911 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
912 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
913}
914
Sadik Armagan15d63e22019-07-26 16:59:35 +0100915bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
916{
917 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100918 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100919}
920
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100921bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
922{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100923 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
924
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100925 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
926 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
927
928 if (!input0.IsValid() || !input1.IsValid())
929 {
930 return Fail("%s: Operation has invalid inputs", __func__);
931 }
932
933 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
934 if (!outputOperand)
935 {
936 return Fail("%s: Could not read output", __func__);
937 }
938
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100939 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100940 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100941 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100942 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100943 }
944
Aron Virginas-Tard7593232019-07-16 13:17:06 +0100945 bool isSupported = false;
946 FORWARD_LAYER_SUPPORT_FUNC(__func__,
947 IsMaximumSupported,
948 data.m_Backends,
949 isSupported,
950 input0.GetTensorInfo(),
951 input1.GetTensorInfo(),
952 outInfo);
953
954 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100955 {
956 return false;
957 }
958
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100959 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100960 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100961 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
962 if (!isReshapeSupported)
963 {
964 return false;
965 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100966
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100967 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100968}
969
Mike Kelly46272802019-08-14 17:00:48 +0100970bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
971{
972 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
973 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
974}
975
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100976bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
977{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100978 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
979
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100980 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
981 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
982
983 if (!input0.IsValid() || !input1.IsValid())
984 {
985 return Fail("%s: Operation has invalid inputs", __func__);
986 }
987
988 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
989 if (!output)
990 {
991 return Fail("%s: Could not read output 0", __func__);
992 }
993
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100994 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100995 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100996 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100997 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100998 }
999
1000 bool isSupported = false;
1001 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1002 IsMinimumSupported,
1003 data.m_Backends,
1004 isSupported,
1005 input0.GetTensorInfo(),
1006 input1.GetTensorInfo(),
1007 outputInfo);
1008
1009 if (!isSupported)
1010 {
1011 return false;
1012 }
1013
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001014 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001015 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001016 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1017 if (!isReshapeSupported)
1018 {
1019 return false;
1020 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001021
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001022 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001023}
1024
Mike Kelly46272802019-08-14 17:00:48 +01001025bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1026{
1027 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1028 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1029}
1030
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001031bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1032{
1033 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1034 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1035}
1036
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001037bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1038{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001039 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1040
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001041 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1042 if (!input.IsValid())
1043 {
1044 return Fail("%s: Could not read input 0", __func__);
1045 }
1046
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001047 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1048 if (!output)
1049 {
1050 return Fail("%s: Could not read output", __func__);
1051 }
1052
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001053 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001054 unsigned int rank = inputInfo.GetNumDimensions();
1055
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001056 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001057 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1058 {
1059 return Fail("%s: Could not convert paddings", __func__);
1060 }
1061
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001062 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001063 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001064 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001065 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001066 }
1067
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001068 // Determine type of padding value
1069 OperandType operandType0;
1070 OperandType operandType2;
1071
1072 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1073 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1074 {
1075 return Fail("%s: Operation has invalid inputs", __func__);
1076 }
1077
1078 // Read value to use for padding
1079 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1080 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001081 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001082 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1083 {
1084 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1085 }
1086
1087 descriptor.m_PadValue = f16PadValue;
1088 }
1089 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1090 {
1091 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1092 {
1093 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1094 }
1095 }
1096 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1097 {
Mike Kelly3c673942019-07-25 09:26:06 +01001098 int32_t intPadValue = 0;
1099 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001100 {
1101 return Fail("%s: Could not read input 2 (INT32)", __func__);
1102 }
Mike Kelly3c673942019-07-25 09:26:06 +01001103 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001104 }
1105 else
1106 {
1107 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1108 }
1109
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001110 bool isSupported = false;
1111 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1112 IsPadSupported,
1113 data.m_Backends,
1114 isSupported,
1115 inputInfo,
1116 outputInfo,
1117 descriptor);
1118 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001119 {
1120 return false;
1121 }
1122
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001123 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001124 assert(layer != nullptr);
1125 input.Connect(layer->GetInputSlot(0));
1126 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1127
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001128 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001129}
1130
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001131bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1132{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001133 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1134
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001135 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1136 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1137
1138 if (!input.IsValid() || !alpha.IsValid())
1139 {
1140 return Fail("%s: Operation has invalid inputs", __func__);
1141 }
1142
1143 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1144
1145 if (!output)
1146 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001147 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001148 }
1149
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001150 const TensorInfo& inputInfo = input.GetTensorInfo();
1151 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1152 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001153
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001154 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001155 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001156 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001157 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001158
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001159 bool isSupported = false;
1160 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1161 IsPreluSupported,
1162 data.m_Backends,
1163 isSupported,
1164 inputInfo,
1165 alphaInfo,
1166 outputInfo);
1167 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001168 {
1169 return false;
1170 }
1171
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001172 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001173
1174 if (!layer)
1175 {
1176 return Fail("%s: AddPreluLayer failed", __func__);
1177 }
1178
Sadik Armagan64b19b52019-08-19 09:49:58 +01001179 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1180 if (!isReshapeSupported)
1181 {
1182 return false;
1183 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001184
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001185 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001186}
1187
Sadik Armagan5a476a82019-07-30 09:43:18 +01001188bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1189{
1190 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1191
1192 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1193 if (!input.IsValid())
1194 {
1195 return Fail("%s: Operation has invalid input", __func__);
1196 }
1197
1198 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1199 if (!outputOperand)
1200 {
1201 return Fail("%s: Operation has invalid outputs", __func__);
1202 }
1203
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001204 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001205 if (IsDynamicTensor(outputInfo))
1206 {
1207 return Fail("%s: Dynamic output tensors are not supported", __func__);
1208 }
1209
1210 bool isSupported = false;
1211 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1212 IsQuantizeSupported,
1213 data.m_Backends,
1214 isSupported,
1215 input.GetTensorInfo(),
1216 outputInfo);
1217 if (!isSupported)
1218 {
1219 return false;
1220 }
1221
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001222 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001223 assert(layer != nullptr);
1224 input.Connect(layer->GetInputSlot(0));
1225
1226 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1227}
1228
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001229bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1230{
1231 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1232
1233 //Inputs:
1234 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1235 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1236 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1237 if (!input.IsValid())
1238 {
1239 return Fail("%s: Could not read input 0: input", __func__);
1240 }
1241
1242 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1243 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1244 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1245 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1246 if (!previousCellStateIn.IsValid())
1247 {
1248 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1249 }
1250
1251 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1252 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1253 // is quantized with a fixed quantization range of -1, 127/128.
1254 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1255 if (!previousOutputIn.IsValid())
1256 {
1257 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1258 }
1259
1260 // Get the input tensors:
1261 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1262 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1263 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1264 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001265 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001266
1267 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1268 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1269 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1270 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001271 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001272
1273 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1274 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1275 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1276 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001277 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001278
1279 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1280 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1281 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1282 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001283 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001284
1285 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1286 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1287 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1288 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001289 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001290
1291 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1292 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1293 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1294 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001295 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001296
1297 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1298 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1299 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1300 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001301 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001302
1303 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1304 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1305 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1306 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001307 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001308
1309 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1310 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1311 // of input and weights scales and zeroPoint equal to 0.
1312 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001313 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001314
1315 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1316 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1317 // of input and weights scales and zeroPoint equal to 0.
1318 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001319 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001320
1321 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1322 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1323 // and weights scales and zeroPoint equal to 0.
1324 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001325 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001326
1327 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1328 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1329 // of input and weights scales and zeroPoint equal to 0.
1330 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001331 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001332
1333 if (!inputToInputWeightsPin.IsValid() ||
1334 !inputToForgetWeightsPin.IsValid() ||
1335 !inputToCellWeightsPin.IsValid() ||
1336 !inputToOutputWeightsPin.IsValid() ||
1337 !recurrentToInputWeightsPin.IsValid() ||
1338 !recurrentToForgetWeightsPin.IsValid() ||
1339 !recurrentToCellWeightsPin.IsValid() ||
1340 !recurrentToOutputWeightsPin.IsValid() ||
1341 !inputGateBiasPin.IsValid() ||
1342 !forgetGateBiasPin.IsValid() ||
1343 !cellBiasPin.IsValid() ||
1344 !outputGateBiasPin.IsValid())
1345 {
1346 return Fail("%s: Operation has invalid tensor inputs", __func__);
1347 }
1348
1349 // Outputs:
1350 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1351 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1352 // of -2^4, 2^4 * 32767/32768.
1353 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1354 if (!cellStateOut)
1355 {
1356 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1357 }
1358
1359 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1360 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1361 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1362 if (!output)
1363 {
1364 return Fail("%s: Could not read output 1: output", __func__);
1365 }
1366
1367 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001368 const TensorInfo& inputInfo = input.GetTensorInfo();
1369 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1370 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001371
1372 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001373 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1374 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001375
1376 // Dynamic tensors currently not supported
1377 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1378 {
1379 return Fail("%s: Dynamic output tensors are not supported", __func__);
1380 }
1381
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001382 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001383
1384 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1385 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1386 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1387 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1388 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1389 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1390 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1391 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1392 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1393 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1394 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1395 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1396
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001397 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001398 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1399 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1400 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1401 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1402 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1403 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1404 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1405 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1406 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1407 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1408 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1409 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1410
1411 bool isSupported = false;
1412 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1413 IsQuantizedLstmSupported,
1414 data.m_Backends,
1415 isSupported,
1416 inputInfo,
1417 previousCellStateInInfo,
1418 previousOutputInInfo,
1419 cellStateOutInfo,
1420 outputInfo,
1421 paramsInfo);
1422
1423 if (!isSupported)
1424 {
1425 return false;
1426 }
1427
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001428 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001429 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001430 previousCellStateIn.Connect(layer->GetInputSlot(1));
1431 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001432
1433 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1434 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1435}
1436
Sadik Armagan61113162019-07-25 09:09:40 +01001437bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1438{
1439 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1440 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1441}
1442
1443bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1444{
1445 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1446 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1447}
1448
1449bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1450{
1451 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1452 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1453}
1454
Mike Kelly46272802019-08-14 17:00:48 +01001455bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1456{
1457 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1458 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1459}
1460
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001461bool HalPolicy::ConvertResize(const Operation& operation,
1462 const Model& model,
1463 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001464 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001465{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001466 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
1467
1468 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001469 if (!input.IsValid())
1470 {
1471 return Fail("%s: Could not read input 0", __func__);
1472 }
1473
1474 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1475 if (!output)
1476 {
1477 return Fail("%s: Could not read output 0", __func__);
1478 }
1479
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001480 const TensorInfo& inputInfo = input.GetTensorInfo();
1481 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001482
1483 if (IsDynamicTensor(outputInfo))
1484 {
1485 return Fail("%s: Dynamic output tensors are not supported", __func__);
1486 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001487
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001488 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001489 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001490 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1491
1492 OperandType operandType1;
1493 OperandType operandType2;
1494
1495 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1496 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1497 {
1498 return Fail("%s: Operation has invalid inputs", __func__);
1499 }
1500
1501 if (operandType1 != operandType2)
1502 {
1503 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1504 }
1505
1506 if (operandType1 == OperandType::INT32)
1507 {
1508 // Case 1: resizing by shape
1509 int32_t targetWidth = 0;
1510 int32_t targetHeight = 0;
1511
1512 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1513 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1514 {
1515 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1516 }
1517
1518 if (targetWidth < 0 || targetHeight < 0)
1519 {
1520 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1521 "Target width/height cannot be < 0", __func__);
1522 }
1523
1524 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001525 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001526 }
1527 else if (operandType1 == OperandType::FLOAT32)
1528 {
1529 // Case 2: resizing by scale
1530 float widthScale = 1.0f;
1531 float heightScale = 1.0f;
1532
1533 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1534 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1535 {
1536 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1537 }
1538
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001539 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001540 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1541
1542 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1543 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1544
1545 descriptor.m_TargetWidth = std::floor(width * widthScale);
1546 descriptor.m_TargetHeight = std::floor(height * heightScale);
1547 }
1548 else
1549 {
1550 // NOTE: FLOAT16 scales are not supported
1551 return false;
1552 }
1553
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001554 bool isSupported = false;
1555 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1556 IsResizeSupported,
1557 data.m_Backends,
1558 isSupported,
1559 inputInfo,
1560 outputInfo,
1561 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001562
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001563 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001564 {
1565 return false;
1566 }
1567
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001568 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001569
1570 assert(layer != nullptr);
1571
1572 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1573 input.Connect(layer->GetInputSlot(0));
1574
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001575 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001576}
1577
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001578bool HalPolicy::ConvertRsqrt(const Operation& operation, const Model& model, ConversionData& data)
1579{
1580 ALOGV("hal_1_2::HalPolicy::ConvertRsqrt()");
1581
1582 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1583 if (!input.IsValid())
1584 {
1585 return Fail("%s: Operation has invalid input", __func__);
1586 }
1587
1588 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1589 if (!output)
1590 {
1591 return Fail("%s: Could not read output 0", __func__);
1592 }
1593
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001594 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001595 if (IsDynamicTensor(outputInfo))
1596 {
1597 return Fail("%s: Dynamic output tensors are not supported", __func__);
1598 }
1599
1600 bool isSupported = false;
1601 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1602 IsRsqrtSupported,
1603 data.m_Backends,
1604 isSupported,
1605 input.GetTensorInfo(),
1606 outputInfo);
1607
1608 if (!isSupported)
1609 {
1610 return false;
1611 }
1612
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001613 IConnectableLayer* const layer = data.m_Network->AddRsqrtLayer();
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001614 assert(layer != nullptr);
1615 input.Connect(layer->GetInputSlot(0));
1616
1617 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1618}
1619
Finn Williamsd74c5052019-07-30 17:06:00 +01001620bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1621{
1622 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1623 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1624}
1625
Keith Davisa6bc52f2019-06-26 09:39:49 +01001626bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1627{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001628 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001629
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001630 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001631 if (!input.IsValid() )
1632 {
1633 return Fail("%s: Operation has invalid inputs", __func__);
1634 }
1635
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001636 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001637 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001638 if (rank != 4)
1639 {
1640 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1641 }
1642
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001643 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1644 if (!output)
1645 {
1646 return Fail("%s: Could not read output 0", __func__);
1647 }
1648
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001649 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001650 if (IsDynamicTensor(outputInfo))
1651 {
1652 return Fail("%s: Dynamic output tensors are not supported", __func__);
1653 }
1654
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001655 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01001656
1657 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1658
1659 if (desc.m_BlockSize <= 1)
1660 {
1661 return Fail("%s: Block size must be at least 1 in all dimensions");
1662 }
1663
1664 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1665
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001666 bool isSupported = false;
1667 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1668 IsSpaceToDepthSupported,
1669 data.m_Backends,
1670 isSupported,
1671 inputInfo,
1672 outputInfo,
1673 desc);
1674 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01001675 {
1676 return false;
1677 }
1678
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001679 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001680 assert(layer != nullptr);
1681 input.Connect(layer->GetInputSlot(0));
1682
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001683 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001684}
1685
Francis Murtagh074c25a2019-07-22 16:40:57 +01001686bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1687{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001688 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1689
Francis Murtagh074c25a2019-07-22 16:40:57 +01001690 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1691 if (!input.IsValid())
1692 {
1693 return Fail("%s: Operation has invalid inputs", __func__);
1694 }
1695
1696 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1697 if (!outputOperand)
1698 {
1699 return Fail("%s: Operation has no outputs", __func__);
1700 }
1701
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001702 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001703 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001704 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001705 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001706 }
1707
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001708 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01001709 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1710 {
1711 return Fail("%s: Operation has invalid inputs", __func__);
1712 }
1713
1714 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1715 2,
1716 HalPolicy::OperandType::INT32,
1717 desc.m_Axis,
1718 model,
1719 data))
1720 {
1721 return Fail("%s: Operation has invalid inputs", __func__);
1722 }
1723
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01001724 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1725 !(desc.m_Axis == 1 ||
1726 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1727 {
1728 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1729 }
1730
Francis Murtagh074c25a2019-07-22 16:40:57 +01001731 bool isSupported = false;
1732 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1733 IsSoftmaxSupported,
1734 data.m_Backends,
1735 isSupported,
1736 input.GetTensorInfo(),
1737 outputInfo,
1738 desc);
1739 if (!isSupported)
1740 {
1741 return false;
1742 }
1743
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001744 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001745 assert(layer != nullptr);
1746 input.Connect(layer->GetInputSlot(0));
1747
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001748 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001749}
1750
Mike Kelly0a879362019-07-29 16:56:31 +01001751bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
1752{
1753 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
1754 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
1755}
1756
Sadik Armagan61113162019-07-25 09:09:40 +01001757bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1758{
1759 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
1760 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
1761}
1762
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001763bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
1764{
1765 // Inputs:
1766 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
1767 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
1768 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1769 if (!input.IsValid())
1770 {
1771 return Fail("%s: Could not read input 0: input", __func__);
1772 }
1773 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1774 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
1775 if (!outputStateIn.IsValid())
1776 {
1777 return Fail("%s: Could not read input 18: outputStateIn", __func__);
1778 }
1779 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1780 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
1781 if (!cellStateIn.IsValid())
1782 {
1783 return Fail("%s: Could not read input 19: cellStateIn", __func__);
1784 }
1785
1786 // Get the mandatory input tensors:
1787 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1788 // [num_units, input_size].
1789 const ConstTensorPin inputToForgetWeightsPin =
1790 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1791 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1792 // [num_units, input_size].
1793 const ConstTensorPin inputToCellWeightsPin =
1794 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
1795 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1796 // [num_units, input_size].
1797 const ConstTensorPin inputToOutputWeightsPin =
1798 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
1799 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1800 // [num_units, output_size].
1801 const ConstTensorPin recurrentToForgetWeightsPin =
1802 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
1803 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1804 // [num_units, output_size].
1805 const ConstTensorPin recurrentToCellWeightsPin =
1806 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
1807 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1808 // [num_units, output_size].
1809 const ConstTensorPin recurrentToOutputWeightsPin =
1810 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
1811 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1812 const ConstTensorPin forgetGateBiasPin =
1813 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
1814 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1815 const ConstTensorPin cellBiasPin =
1816 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
1817 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1818 const ConstTensorPin outputGateBiasPin =
1819 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
1820
1821 if (!inputToForgetWeightsPin.IsValid() ||
1822 !inputToCellWeightsPin.IsValid() ||
1823 !inputToOutputWeightsPin.IsValid() ||
1824 !recurrentToForgetWeightsPin.IsValid() ||
1825 !recurrentToCellWeightsPin.IsValid() ||
1826 !recurrentToOutputWeightsPin.IsValid() ||
1827 !forgetGateBiasPin.IsValid() ||
1828 !cellBiasPin.IsValid() ||
1829 !outputGateBiasPin.IsValid())
1830 {
1831 return Fail("%s: Operation has invalid tensor inputs", __func__);
1832 }
1833
1834 // Get the optional input tensors:
1835 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1836 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
1837 const ConstTensorPin inputToInputWeightsPin =
1838 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1839 1,
1840 model,
1841 data,
1842 g_DontPermute,
1843 nullptr,
1844 true);
1845
1846 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1847 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
1848 // “num_units”), or the second dimension of the “projection_weights”, if defined.
1849 const ConstTensorPin recurrentToInputWeightsPin =
1850 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1851 5,
1852 model,
1853 data,
1854 g_DontPermute,
1855 nullptr,
1856 true);
1857
1858 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1859 const ConstTensorPin cellToInputWeightsPin =
1860 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1861 9,
1862 model,
1863 data,
1864 g_DontPermute,
1865 nullptr,
1866 true);
1867
1868 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1869 const ConstTensorPin cellToForgetWeightsPin =
1870 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1871 10,
1872 model,
1873 data,
1874 g_DontPermute,
1875 nullptr,
1876 true);
1877
1878 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1879 const ConstTensorPin cellToOutputWeightsPin =
1880 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1881 11,
1882 model,
1883 data,
1884 g_DontPermute,
1885 nullptr,
1886 true);
1887
1888 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1889 const ConstTensorPin inputGateBiasPin =
1890 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1891 12,
1892 model,
1893 data,
1894 g_DontPermute,
1895 nullptr,
1896 true);
1897
1898 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1899 // [output_size, num_units].
1900 const ConstTensorPin projectionWeightsPin =
1901 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1902 16,
1903 model,
1904 data,
1905 g_DontPermute,
1906 nullptr,
1907 true);
1908
1909 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
1910 const ConstTensorPin projectionBiasPin =
1911 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1912 17,
1913 model,
1914 data,
1915 g_DontPermute,
1916 nullptr,
1917 true);
1918
1919 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
1920 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
1921 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
1922 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
1923 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
1924 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
1925 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
1926 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
1927 {
1928 return Fail("%s: Operation has invalid tensor inputs", __func__);
1929 }
1930
1931 // Get the mandatory input scalars (actually 1-D tensors of size 1):
1932 // 20: The activation function: A value indicating the activation function:
1933 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
1934 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
1935 // If set to 0.0 then clipping is disabled.
1936 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
1937 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
1938 ActivationFn activation;
1939 float cellClip;
1940 float projClip;
1941 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
1942 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
1943 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
1944 {
1945 return Fail("%s: Operation has invalid scalar inputs", __func__);
1946 }
1947
1948 // Get the normalization tensors
1949 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
1950 // Used to rescale normalized inputs to activation at input gate.
1951 const ConstTensorPin inputLayerNormWeightsPin =
1952 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1953 23,
1954 model,
1955 data,
1956 g_DontPermute,
1957 nullptr,
1958 true);
1959
1960 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
1961 // Used to rescale normalized inputs to activation at forget gate.
1962 const ConstTensorPin forgetLayerNormWeightsPin =
1963 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1964 24,
1965 model,
1966 data,
1967 g_DontPermute,
1968 nullptr,
1969 true);
1970
1971 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
1972 // Used to rescale normalized inputs to activation at cell gate.
1973 const ConstTensorPin cellLayerNormWeightsPin =
1974 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1975 25,
1976 model,
1977 data,
1978 g_DontPermute,
1979 nullptr,
1980 true);
1981
1982 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
1983 // Used to rescale normalized inputs to activation at output gate.
1984 const ConstTensorPin outputLayerNormWeightsPin =
1985 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1986 26,
1987 model,
1988 data,
1989 g_DontPermute,
1990 nullptr,
1991 true);
1992
1993 // Outputs:
1994 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
1995 // with CIFG, or [batch_size, num_units * 3] without CIFG.
1996 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1997 if (!scratchBuffer)
1998 {
1999 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2000 }
2001 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2002 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2003 if (!outputStateOut)
2004 {
2005 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2006 }
2007 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2008 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2009 if (!cellStateOut)
2010 {
2011 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2012 }
2013 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2014 // effectively the same as the current “output state (out)” value.
2015 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2016 if (!output)
2017 {
2018 return Fail("%s: Could not read output 3: output", __func__);
2019 }
2020
2021 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002022 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002023 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2024 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2025 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2026 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2027 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2028 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2029 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2030 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2031 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2032 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2033 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2034 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2035 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2036 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2037 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2038 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2039 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2040 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2041 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2042 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2043 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2044
2045 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002046 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002047 desc.m_ActivationFunc = activation;
2048 desc.m_ClippingThresCell = cellClip;
2049 desc.m_ClippingThresProj = projClip;
2050 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2051 params.m_RecurrentToInputWeights == nullptr ||
2052 params.m_InputGateBias == nullptr);
2053 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2054 params.m_CellToOutputWeights != nullptr);
2055 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2056 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2057 params.m_ForgetLayerNormWeights != nullptr ||
2058 params.m_CellLayerNormWeights != nullptr ||
2059 params.m_OutputLayerNormWeights != nullptr);
2060
2061 // validate the optional input groups
2062 if (desc.m_CifgEnabled &&
2063 (params.m_InputToInputWeights != nullptr ||
2064 params.m_RecurrentToInputWeights != nullptr ||
2065 params.m_InputGateBias != nullptr))
2066 {
2067 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2068 " and input gate bias must be provided", __func__);
2069 }
2070
2071 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2072 {
2073 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2074 }
2075
2076 if (desc.m_PeepholeEnabled &&
2077 (params.m_CellToForgetWeights == nullptr ||
2078 params.m_CellToOutputWeights == nullptr ||
2079 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2080 {
2081 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2082 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2083 }
2084
2085 if (desc.m_LayerNormEnabled &&
2086 (params.m_ForgetLayerNormWeights == nullptr ||
2087 params.m_CellLayerNormWeights == nullptr ||
2088 params.m_OutputLayerNormWeights == nullptr ||
2089 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2090 {
2091 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2092 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2093 }
2094
2095 // Check if the layer is supported
2096 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002097 const TensorInfo& inputInfo = input.GetTensorInfo();
2098 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2099 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002100
2101 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002102 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2103 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2104 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2105 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002106
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002107 if (IsDynamicTensor(scratchBufferInfo) ||
2108 IsDynamicTensor(outputStateOutInfo) ||
2109 IsDynamicTensor(cellStateOutInfo) ||
2110 IsDynamicTensor(outputInfo))
2111 {
2112 return Fail("%s: Dynamic output tensors are not supported", __func__);
2113 }
2114
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002115 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002116 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002117 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2118 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2119 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2120 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2121 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2122 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2123 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2124 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2125 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2126
2127 // Optional parameters
2128 if(!desc.m_CifgEnabled)
2129 {
2130 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2131 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2132 if (params.m_CellToInputWeights != nullptr)
2133 {
2134 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2135 }
2136 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2137 }
2138
2139 if(desc.m_ProjectionEnabled)
2140 {
2141 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2142 if (params.m_ProjectionBias != nullptr)
2143 {
2144 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2145 }
2146 }
2147
2148 if(desc.m_PeepholeEnabled)
2149 {
2150 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2151 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2152 }
2153
2154 if (desc.m_LayerNormEnabled)
2155 {
2156 if(!desc.m_CifgEnabled)
2157 {
2158 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2159 }
2160 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2161 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2162 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2163 }
2164
2165 bool isSupported = false;
2166 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2167 IsLstmSupported,
2168 data.m_Backends,
2169 isSupported,
2170 inputInfo,
2171 outputStateInInfo,
2172 cellStateInInfo,
2173 scratchBufferInfo,
2174 outputStateOutInfo,
2175 cellStateOutInfo,
2176 outputInfo,
2177 desc,
2178 paramsInfo);
2179 if (!isSupported)
2180 {
2181 return false;
2182 }
2183
2184 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002185 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002186
2187 input.Connect(layer->GetInputSlot(0));
2188 outputStateIn.Connect(layer->GetInputSlot(1));
2189 cellStateIn.Connect(layer->GetInputSlot(2));
2190
2191 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
2192 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2193 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2194 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2195}
2196
Sadik Armagan701d9a02019-09-04 15:16:18 +01002197bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2198{
2199 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002200 ActivationDescriptor desc;
2201 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002202
2203 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2204}
2205
Mike Kelly46272802019-08-14 17:00:48 +01002206bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2207{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002208 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002209 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2210}
2211
2212bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2213{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002214 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002215 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2216}
2217
2218bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2219{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002220 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002221 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2222}
2223
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002224bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002225{
2226 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2227
2228 if (!input.IsValid())
2229 {
2230 return Fail("%s: Operation has invalid inputs", __func__);
2231 }
2232
2233 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2234
2235 if (!output)
2236 {
2237 return Fail("%s: Could not read output 0", __func__);
2238 }
2239
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002240 const TensorInfo& inputInfo = input.GetTensorInfo();
2241 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002242 if (IsDynamicTensor(outputInfo))
2243 {
2244 return Fail("%s: Dynamic output tensors are not supported", __func__);
2245 }
2246
2247 // ArmNN does not currently support non-fixed weights or bias
2248 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2249 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2250
2251 if (weightsOperand == nullptr)
2252 {
2253 return Fail("%s: Operand is invalid", __func__);
2254 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002255 TransposeConvolution2dDescriptor desc;
2256 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002257
2258 // Determine whether padding is implicit or explicit
2259 bool implicitPadding = operation.inputs.size() == 9;
2260
2261 if (implicitPadding )
2262 {
2263 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2264 }
2265 else
2266 {
2267 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2268 }
2269
2270 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2271 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2272 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2273
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002274 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002275
2276 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2277 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002278 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002279 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2280 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2281
2282 // Bias is a 1D tensor
2283 const ConstTensorPin biasPin =
2284 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2285
2286 if (!weightsPin.IsValid())
2287 {
2288 return Fail("%s: Operation has invalid weights", __func__);
2289 }
2290
2291 if (!biasPin.IsValid())
2292 {
2293 return Fail("%s: Operation has invalid biases", __func__);
2294 }
2295
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002296 ConstTensor weights = weightsPin.GetConstTensor();
2297 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002298 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2299
2300 ActivationFn activation;
2301
2302 if (implicitPadding)
2303 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002304 int32_t strideX{0};
2305 int32_t strideY{0};
2306 int32_t padLeft{0};
2307 int32_t padRight{0};
2308 int32_t padTop{0};
2309 int32_t padBottom{0};
2310
David Monahan613b49c2019-06-27 11:37:47 +01002311 android::nn::PaddingScheme paddingScheme;
2312 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002313 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2314 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002315 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2316 {
2317 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2318 }
2319
2320 const uint32_t kernelX = weights.GetShape()[widthIndex];
2321 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002322 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2323 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002324
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002325 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2326 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2327
2328 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2329 // but Arm NN only supports values >= 0
2330 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2331 {
2332 return Fail("%s: Negative padding values are not supported", __func__);
2333 }
2334
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002335 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2336 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002337 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2338 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2339 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2340 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002341 }
2342 else if (operation.inputs.size() == 11)
2343 {
2344 // explicit padding
2345 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2346 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2347 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2348 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2349 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2350 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2351 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2352 {
2353 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2354 }
2355 }
2356 else
2357 {
2358 return Fail("%s: Unsupported number of operation inputs", __func__);
2359 }
2360
2361 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002362 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002363
2364 bool isSupported = false;
2365 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2366 IsTransposeConvolution2dSupported,
2367 data.m_Backends,
2368 isSupported,
2369 inputInfo,
2370 outputInfo,
2371 desc,
2372 weights.GetInfo(),
2373 biases);
2374 if (!isSupported)
2375 {
2376 return false;
2377 }
2378
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002379 IConnectableLayer* startLayer =
2380 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002381 if (!startLayer)
2382 {
2383 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2384 }
2385
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002386 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002387 if (!endLayer)
2388 {
2389 return Fail("%s: ProcessActivation failed", __func__);
2390 }
2391
2392 input.Connect(startLayer->GetInputSlot(0));
2393
2394 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2395}
2396
Mike Kellyb5fdf382019-06-11 16:35:25 +01002397} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002398} // namespace armnn_driver