blob: 019f50543d202801f92cd29e4bd29b2bcd60f026 [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);
Aron Virginas-Tara2a73802019-10-09 15:30:40 +010057 case V1_2::OperationType::INSTANCE_NORMALIZATION:
58 return ConvertInstanceNormalization(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010059 case V1_2::OperationType::L2_NORMALIZATION:
60 return ConvertL2Normalization(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010061 case V1_2::OperationType::L2_POOL_2D:
62 return ConvertL2Pool2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010063 case V1_2::OperationType::LOCAL_RESPONSE_NORMALIZATION:
64 return ConvertLocalResponseNormalization(operation, model, data);
65 case V1_2::OperationType::LOGISTIC:
66 return ConvertLogistic(operation, model, data);
67 case V1_2::OperationType::LSTM:
68 return ConvertLstm(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010069 case V1_2::OperationType::MAX_POOL_2D:
70 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +010071 case V1_2::OperationType::MAXIMUM:
72 return ConvertMaximum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010073 case V1_2::OperationType::MEAN:
74 return ConvertMean(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +010075 case V1_2::OperationType::MINIMUM:
76 return ConvertMinimum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010077 case V1_2::OperationType::MUL:
78 return ConvertMul(operation, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +010079 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +010080 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +010081 case V1_2::OperationType::PAD_V2:
82 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +010083 case V1_2::OperationType::PRELU:
84 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +010085 case V1_2::OperationType::QUANTIZE:
86 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +010087 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
88 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +010089 case V1_2::OperationType::RELU:
90 return ConvertReLu(operation, model, data);
91 case V1_2::OperationType::RELU1:
92 return ConvertReLu1(operation, model, data);
93 case V1_2::OperationType::RELU6:
94 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010095 case V1_2::OperationType::RESHAPE:
96 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +010097 case V1_2::OperationType::RESIZE_BILINEAR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +010098 return ConvertResize(operation, model, data, ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010099 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100100 return ConvertResize(operation, model, data, ResizeMethod::NearestNeighbor);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +0100101 case V1_2::OperationType::RSQRT:
102 return ConvertRsqrt(operation, model, data);
Sadik Armagan701d9a02019-09-04 15:16:18 +0100103 case V1_2::OperationType::SQRT:
104 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100105 case V1_2::OperationType::SQUEEZE:
106 return ConvertSqueeze(operation, model, data);
107 case V1_2::OperationType::STRIDED_SLICE:
108 return ConvertStridedSlice(operation, model, data);
109 case V1_2::OperationType::TRANSPOSE:
110 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +0100111 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +0100112 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +0100113 case V1_2::OperationType::SOFTMAX:
114 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +0100115 case V1_2::OperationType::SPACE_TO_BATCH_ND :
116 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100117 case V1_2::OperationType::SPACE_TO_DEPTH:
118 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100119 case V1_2::OperationType::SUB:
120 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100121 case V1_2::OperationType::TANH:
122 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100123 default:
124 return Fail("%s: Operation type %s not supported in ArmnnDriver",
125 __func__, toString(operation.type).c_str());
126 }
127}
128
Kevin May407718f2019-09-09 14:46:41 +0100129bool HalPolicy::ConvertAbs(const Operation& operation, const Model& model, ConversionData& data)
130{
131 ALOGV("hal_1_2::HalPolicy::ConvertAbs()");
132 return ::ConvertAbs<hal_1_2::HalPolicy>(operation, model, data);
133}
134
Mike Kelly46272802019-08-14 17:00:48 +0100135bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
136{
137 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
138 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
139}
140
Sadik Armagan15d63e22019-07-26 16:59:35 +0100141bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
142{
143 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100144 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Average, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100145}
146
Finn Williams23b87b32019-07-30 11:44:05 +0100147bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
148{
149 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
150 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
151}
152
Mike Kellyb8805202019-07-31 17:25:43 +0100153bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
154{
155 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
156 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
157}
158
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100159bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
160{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100161 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
162
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100163 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
164 if (!input.IsValid())
165 {
166 return Fail("%s: Operation has invalid inputs", __func__);
167 }
168
169 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
170 if (!output)
171 {
172 return Fail("%s: Could not read output 0", __func__);
173 }
174
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100175 const TensorInfo& inputInfo = input.GetTensorInfo();
176 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100177
178 if (IsDynamicTensor(outputInfo))
179 {
180 return Fail("%s: Dynamic output tensors are not supported", __func__);
181 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100182
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100183 Convolution2dDescriptor desc;
184 desc.m_DataLayout = DataLayout::NHWC;
Mike Kellye1d60bb2019-07-11 11:44:52 +0100185
186 // Determine whether padding is implicit or explicit
187 bool implicitPadding = operation.inputs.size() == 7 ||
188 (operation.inputs.size() >= 8 &&
189 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
190
191 if (implicitPadding)
192 {
193 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
194 }
195 else if (operation.inputs.size() >= 10)
196 {
197 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
198 }
199
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100200 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
Mike Kellye1d60bb2019-07-11 11:44:52 +0100201
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100202 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100203 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
204 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
205 // the DataLayout is NCHW
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100206 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
Mike Kellye1d60bb2019-07-11 11:44:52 +0100207 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
208 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100209 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100210 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100211
212 if (!weightsPin.IsValid())
213 {
214 return Fail("%s: Operation has invalid weights", __func__);
215 }
216
217 if (!biasPin.IsValid())
218 {
219 return Fail("%s: Operation has invalid biases", __func__);
220 }
221
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100222 ConstTensor weights = weightsPin.GetConstTensor();
223 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100224 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
225
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100226 ActivationFn activation;
227
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100228 if (implicitPadding)
229 {
230 android::nn::PaddingScheme paddingScheme;
231 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
232 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
233 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
234 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
235 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
236 {
237 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
238 }
239
Mike Kellye1d60bb2019-07-11 11:44:52 +0100240 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
241 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
242 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
243 const uint32_t kernelX = weights.GetShape()[widthIndex];
244 const uint32_t kernelY = weights.GetShape()[heightIndex];
245 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
246 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100247
Mike Kelly86b36d42019-07-12 16:39:33 +0100248 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
249 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100250
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100251 }
252 else if (operation.inputs.size() >= 10)
253 {
254 // explicit padding
255 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
256 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
257 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
258 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
259 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
260 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
261 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
262 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
263 {
264 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
265 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100266 }
267 else
268 {
269 return Fail("%s: Unsupported number of operation inputs", __func__);
270 }
271
272 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100273 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100274
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100275 bool isSupported = false;
276 FORWARD_LAYER_SUPPORT_FUNC(__func__,
277 IsConvolution2dSupported,
278 data.m_Backends,
279 isSupported,
280 inputInfo,
281 outputInfo,
282 desc,
283 weights.GetInfo(),
284 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100285
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100286 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100287 {
288 return false;
289 }
290
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100291 IConnectableLayer* startLayer =
292 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100293
294 if (!startLayer)
295 {
296 return Fail("%s: AddConvolution2dLayer failed", __func__);
297 }
298
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100299 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100300
301 if (!endLayer)
302 {
303 return Fail("%s: ProcessActivation failed", __func__);
304 }
305
306 input.Connect(startLayer->GetInputSlot(0));
307
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100308 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100309}
310
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100311bool HalPolicy::ConvertDepthToSpace(const Operation& operation, const Model& model, ConversionData& data)
312{
313 ALOGV("hal_1_2::HalPolicy::ConvertDepthToSpace()");
314 return ::ConvertDepthToSpace<hal_1_2::HalPolicy>(operation, model, data);
315}
316
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100317bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
318{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100319 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
320
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100321 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
322
323 if (!input.IsValid())
324 {
325 return Fail("%s: Operation has invalid inputs", __func__);
326 }
327
328 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
329
330 if (!output)
331 {
332 return Fail("%s: Could not read output 0", __func__);
333 }
334
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100335 const TensorInfo& inputInfo = input.GetTensorInfo();
336 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100337
338 if (IsDynamicTensor(outputInfo))
339 {
340 return Fail("%s: Dynamic output tensors are not supported", __func__);
341 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100342
343 // ArmNN does not currently support non-fixed weights or bias
344 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
345 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
346
347 if (weightsOperand == nullptr)
348 {
349 return Fail("%s: Operand is invalid", __func__);
350 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100351 DepthwiseConvolution2dDescriptor desc;
352 desc.m_DataLayout = DataLayout::NHWC;
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100353
354 // Determine whether padding is implicit or explicit
355 bool implicitPadding = operation.inputs.size() == 8 ||
356 (operation.inputs.size() >= 9 &&
357 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
358
359 // Look ahead to find the optional DataLayout, if present
360 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
361 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
362
363 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
364 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
365 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
366 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
367
368 // Reinterpret weight data as [ H, W, I, M ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100369 TensorShape weightsShape({ weightsOperand->dimensions[1],
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100370 weightsOperand->dimensions[2],
371 inputInfo.GetShape()[channelsIndex],
372 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
373
374 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100375 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100376
377 const ConstTensorPin weightsPin =
378 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
379 1,
380 model,
381 data,
382 HWIMToMIHW,
383 &weightsShape);
384
385 // Bias is a 1D tensor
386 const ConstTensorPin biasPin =
387 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
388
389 if (!weightsPin.IsValid())
390 {
391 return Fail("%s: Operation has invalid weights", __func__);
392 }
393
394 if (!biasPin.IsValid())
395 {
396 return Fail("%s: Operation has invalid biases", __func__);
397 }
398
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100399 ConstTensor weights = weightsPin.GetConstTensor();
400 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100401 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
402
403 ActivationFn activation;
404
405 if (implicitPadding)
406 {
407 android::nn::PaddingScheme paddingScheme;
408 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
409 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
410 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
411 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
412 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
413 {
414 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
415 }
416
417 const uint32_t kernelX = weights.GetShape()[3];
418 const uint32_t kernelY = weights.GetShape()[2];
419 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
420 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
421
Mike Kelly86b36d42019-07-12 16:39:33 +0100422 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
423 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100424 }
425 else if (operation.inputs.size() >= 11)
426 {
427 // explicit padding
428 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
429 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
430 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
431 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
432 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
433 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
434 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
435 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
436 {
437 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
438 }
439 }
440 else
441 {
442 return Fail("%s: Unsupported number of operation inputs", __func__);
443 }
444
445 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100446 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100447
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100448 bool isSupported = false;
449 FORWARD_LAYER_SUPPORT_FUNC(__func__,
450 IsDepthwiseConvolutionSupported,
451 data.m_Backends,
452 isSupported,
453 inputInfo,
454 outputInfo,
455 desc,
456 weights.GetInfo(),
457 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100458
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100459 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100460 {
461 return false;
462 }
463
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100464 IConnectableLayer* startLayer =
465 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100466
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100467 if (!startLayer)
468 {
469 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
470 }
471
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100472 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100473 if (!endLayer)
474 {
475 return Fail("%s: ProcessActivation failed", __func__);
476 }
477
478 input.Connect(startLayer->GetInputSlot(0));
479
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100480 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100481}
482
Mike Kelly46272802019-08-14 17:00:48 +0100483bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
484{
485 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
486 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
487}
488
489bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
490{
491 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
492 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
493}
494
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100495bool HalPolicy::ConvertExpandDims(const Operation& operation, const Model& model, ConversionData& data)
496{
497 ALOGV("hal_1_2::HalPolicy::ConvertExpandDims()");
498
499 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
500
501 if (!input.IsValid())
502 {
503 return Fail("%s: Operation has invalid input", __func__);
504 }
505
506 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
507 if (!output)
508 {
509 return Fail("%s: Operation has invalid output", __func__);
510 }
511
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100512 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100513 if (IsDynamicTensor(outputInfo))
514 {
515 return Fail("%s: Dynamic output tensors are not supported", __func__);
516 }
517
518 int32_t axis;
519 if (!GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, axis, model, data))
520 {
521 return Fail("%s: failed to get axis input value", __func__);
522 }
523
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100524 TensorShape targetShape;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100525
526 try
527 {
528 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
529 }
530 catch (const std::exception &e)
531 {
532 return Fail("%s: %s", __func__, e.what());
533 }
534
535 if (targetShape != outputInfo.GetShape())
536 {
537 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
538 }
539
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100540 ReshapeDescriptor reshapeDescriptor;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100541 reshapeDescriptor.m_TargetShape = targetShape;
542
543 bool isSupported = false;
544 FORWARD_LAYER_SUPPORT_FUNC(__func__,
545 IsReshapeSupported,
546 data.m_Backends,
547 isSupported,
548 input.GetTensorInfo(),
549 reshapeDescriptor);
550
551 if (!isSupported)
552 {
553 return false;
554 }
555
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100556 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100557 assert(layer != nullptr);
558 input.Connect(layer->GetInputSlot(0));
559
560 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
561}
562
Mike Kelly46272802019-08-14 17:00:48 +0100563bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
564{
565 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
566 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
567}
568
569bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
570{
571 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
572 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
573}
574
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100575bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
576{
577 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
578
579 //
580 // Parse data
581 //
582 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
583 if (!input.IsValid())
584 {
585 return Fail("%s: Operation has invalid inputs", __func__);
586 }
587 const TensorInfo& inputInfo = input.GetTensorInfo();
588
589 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
590 if (!output)
591 {
592 return Fail("%s: Could not read output 0", __func__);
593 }
594 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
595 if (IsDynamicTensor(outputInfo))
596 {
597 return Fail("%s: Dynamic output tensors are not supported", __func__);
598 }
599
600 // Look ahead to determine data layout
601 DataLayout dataLayout = DataLayout::NHWC;
602 if (operation.inputs.size() == 12)
603 {
604 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
605 }
606 else
607 {
608 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
609 }
610
611 // NOTE:
612 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
613 // but Arm NN expects the filter's height and width indices to match the input's height and
614 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
615 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
616 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
617 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
618 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
619 const ConstTensorPin biasesPin =
620 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
621 if (!weightsPin.IsValid() || !biasesPin.IsValid())
622 {
623 return Fail("%s: Operation has invalid inputs", __func__);
624 }
625
626 ConstTensor weights = weightsPin.GetConstTensor();
627 ConstTensor biases = biasesPin.GetConstTensor();
628 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
629
630 const TensorShape& inputShape = inputInfo.GetShape();
631 const TensorShape& outputShape = outputInfo.GetShape();
632 const TensorShape& weightsShape = weights.GetShape();
633 const TensorShape& biasesShape = biases.GetShape();
634
635 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
636 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
637 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
638 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
639
640 Convolution2dDescriptor desc;
641 desc.m_DataLayout = dataLayout;
642 desc.m_BiasEnabled = true;
643
644 int numGroups;
645 ActivationFn activation;
646
647 if (operation.inputs.size() == 12)
648 {
649 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
650 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
651 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
652 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
653 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
654 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
655 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
656 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
657 {
658 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
659 }
660
661 }
662 else if (operation.inputs.size() == 9)
663 {
664 android::nn::PaddingScheme paddingScheme;
665 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
666 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
667 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
668 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
669 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
670 {
671 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
672 }
673
674 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
675 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
676
677 const uint32_t kernelX = weightsShape[widthIndex];
678 const uint32_t kernelY = weightsShape[heightIndex];
679
680 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
681 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
682 }
683 else
684 {
685 return Fail("%s: Unsupported number of operation inputs", __func__);
686 }
687
688 const unsigned int outputChannels = outputShape[channelsIndex];
689
690 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
691 const unsigned int channelMultiplier = outputChannels / numGroups;
692
693 //
694 // Validate all relevant inputs
695 //
696 if (numGroups <= 0)
697 {
698 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
699 }
700
701 if (outputChannels % numGroups != 0u)
702 {
703 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
704 }
705
706 //
707 // Set up Splitter layer
708 //
709 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
710 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
711
712 TensorInfo splitterOutputInfo(4,
713 splitterDimSizes,
714 inputInfo.GetDataType(),
715 inputInfo.GetQuantizationScale(),
716 inputInfo.GetQuantizationOffset());
717
718 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
719
720 ViewsDescriptor splitterDesc(numGroups);
721 for (unsigned int group = 0u; group < numGroups; ++group)
722 {
723 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
724 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
725 {
726 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
727 }
728 }
729
730 bool isSupported = false;
731 FORWARD_LAYER_SUPPORT_FUNC(__func__,
732 IsSplitterSupported,
733 data.m_Backends,
734 isSupported,
735 inputInfo,
736 splitterOutputInfos,
737 splitterDesc);
738 if (!isSupported)
739 {
740 return false;
741 }
742
743 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
744 if (!splitterLayer)
745 {
746 return Fail("%s: Failed to add SplitterLayer", __func__);
747 }
748
749 input.Connect(splitterLayer->GetInputSlot(0));
750 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
751 {
752 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
753 }
754
755 //
756 // Set up Convolution2d layers for each group
757 //
758 TensorShape groupInputShape(inputShape);
759 groupInputShape[channelsIndex] = channelsPerGroup;
760
761 TensorShape groupOutputShape(outputShape);
762 groupOutputShape[channelsIndex] = 1;
763
764 TensorShape groupWeightsShape(weightsShape);
765 groupWeightsShape[0] /= channelMultiplier * numGroups;
766
767 TensorShape groupBiasesShape({ 1 });
768
769 const TensorInfo groupInputInfo (groupInputShape,
770 inputInfo.GetDataType(),
771 inputInfo.GetQuantizationScale(),
772 inputInfo.GetQuantizationOffset());
773 const TensorInfo groupWeightsInfo(groupWeightsShape,
774 weights.GetInfo().GetDataType(),
775 weights.GetInfo().GetQuantizationScale(),
776 weights.GetInfo().GetQuantizationOffset());
777 const TensorInfo groupBiasesInfo (groupBiasesShape,
778 biases.GetInfo().GetDataType(),
779 biases.GetInfo().GetQuantizationScale(),
780 biases.GetInfo().GetQuantizationOffset());
781 const TensorInfo groupOutputInfo (groupOutputShape,
782 outputInfo.GetDataType(),
783 outputInfo.GetQuantizationScale(),
784 outputInfo.GetQuantizationOffset());
785
786 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
787 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
788
789 std::vector<IConnectableLayer*> convLayers(numGroups*channelMultiplier, nullptr);
790 for (unsigned int group = 0u; group < numGroups; ++group)
791 {
792 for (unsigned int m = 0u; m < channelMultiplier; ++m)
793 {
794 auto index = group * channelMultiplier + m;
795
796 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
797 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
798
799 // Extract weights and biases data for current group convolution
800 ConstTensor groupWeights(groupWeightsInfo,
801 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
802 weightsDataOffset));
803 ConstTensor groupBiases(groupBiasesInfo,
804 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
805 biasesDataOffset));
806
807 isSupported = false;
808 FORWARD_LAYER_SUPPORT_FUNC(__func__,
809 IsConvolution2dSupported,
810 data.m_Backends,
811 isSupported,
812 groupInputInfo,
813 groupOutputInfo,
814 desc,
815 groupWeightsInfo,
816 Optional<TensorInfo>(groupBiasesInfo));
817 if (!isSupported)
818 {
819 return false;
820 }
821
822 IConnectableLayer *convLayer =
823 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
824 if (!convLayer)
825 {
826 return Fail("%s: AddConvolution2dLayer failed", __func__);
827 }
828
829 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
830 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
831
832 convLayers[index] = convLayer;
833 }
834 }
835
836 //
837 // Set up Concat layer
838 //
839 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
840 for (unsigned int group = 0u; group < numGroups; ++group)
841 {
842 for (unsigned int m = 0u; m < channelMultiplier; ++m)
843 {
844 auto index = group * channelMultiplier + m;
845 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
846 concatDescriptor.SetConcatAxis(channelsIndex);
847 }
848 }
849
850 isSupported = false;
851 FORWARD_LAYER_SUPPORT_FUNC(__func__,
852 IsConcatSupported,
853 data.m_Backends,
854 isSupported,
855 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
856 outputInfo,
857 concatDescriptor);
858 if (!isSupported)
859 {
860 return false;
861 }
862
863 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
864 if (!concatLayer)
865 {
866 return Fail("%s: AddConcatLayer failed", __func__);
867 }
868
869 for (unsigned int group = 0u; group < numGroups; ++group)
870 {
871 for (unsigned int m = 0u; m < channelMultiplier; ++m)
872 {
873 auto index = group * channelMultiplier + m;
874 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
875 }
876 }
877 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
878
879 //
880 // Set up Activation layer (if it is set)
881 //
882 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
883 if (!endLayer)
884 {
885 return Fail("%s: ProcessActivation failed", __func__);
886 }
887
888 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
889}
890
Aron Virginas-Tara2a73802019-10-09 15:30:40 +0100891bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
892{
893 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
894
895 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
896 if (!input.IsValid())
897 {
898 return Fail("%s: Operation has an invalid input 0", __func__);
899 }
900
901 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
902 if (!output)
903 {
904 return Fail("%s: Operation has an invalid output", __func__);
905 }
906
907 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
908 if (IsDynamicTensor(outputInfo))
909 {
910 return Fail("%s: Dynamic output tensors are not supported", __func__);
911 }
912
913 // Determine data type of input tensor
914 OperandType inputType;
915 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
916 {
917 return Fail("%s: Operation has invalid inputs", __func__);
918 }
919
920 InstanceNormalizationDescriptor desc;
921
922 // Read gamma, beta & epsilon
923 if (inputType == OperandType::TENSOR_FLOAT16)
924 {
925 Half fp16Gamma;
926 Half fp16Beta;
927 Half fp16Epsilon;
928
929 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
930 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
931 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
932 {
933 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
934 }
935
936 desc.m_Gamma = static_cast<float>(fp16Gamma);
937 desc.m_Beta = static_cast<float>(fp16Beta);
938 desc.m_Eps = static_cast<float>(fp16Epsilon);
939 }
940 else if (inputType == OperandType::TENSOR_FLOAT32)
941 {
942 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
943 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
944 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
945 {
946 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
947 }
948 }
949 else
950 {
951 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
952 }
953
954 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
955
956 bool isSupported = false;
957 FORWARD_LAYER_SUPPORT_FUNC(__func__,
958 IsInstanceNormalizationSupported,
959 data.m_Backends,
960 isSupported,
961 input.GetTensorInfo(),
962 outputInfo,
963 desc);
964 if (!isSupported)
965 {
966 return false;
967 }
968
969 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
970 input.Connect(layer->GetInputSlot(0));
971
972 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
973}
974
Mike Kelly46272802019-08-14 17:00:48 +0100975bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
976{
977 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
978 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
979}
980
Sadik Armagan15d63e22019-07-26 16:59:35 +0100981bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
982{
983 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100984 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100985}
986
Mike Kelly46272802019-08-14 17:00:48 +0100987bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
988 const Model& model,
989 ConversionData& data)
990{
991 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
992 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
993}
994
995bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
996{
997 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
998 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
999}
1000
Sadik Armagan15d63e22019-07-26 16:59:35 +01001001bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1002{
1003 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001004 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001005}
1006
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001007bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1008{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001009 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1010
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001011 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1012 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1013
1014 if (!input0.IsValid() || !input1.IsValid())
1015 {
1016 return Fail("%s: Operation has invalid inputs", __func__);
1017 }
1018
1019 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1020 if (!outputOperand)
1021 {
1022 return Fail("%s: Could not read output", __func__);
1023 }
1024
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001025 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001026 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001027 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001028 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001029 }
1030
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001031 bool isSupported = false;
1032 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1033 IsMaximumSupported,
1034 data.m_Backends,
1035 isSupported,
1036 input0.GetTensorInfo(),
1037 input1.GetTensorInfo(),
1038 outInfo);
1039
1040 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001041 {
1042 return false;
1043 }
1044
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001045 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001046 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001047 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1048 if (!isReshapeSupported)
1049 {
1050 return false;
1051 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001052
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001053 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001054}
1055
Mike Kelly46272802019-08-14 17:00:48 +01001056bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1057{
1058 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1059 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1060}
1061
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001062bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1063{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001064 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1065
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001066 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1067 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1068
1069 if (!input0.IsValid() || !input1.IsValid())
1070 {
1071 return Fail("%s: Operation has invalid inputs", __func__);
1072 }
1073
1074 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1075 if (!output)
1076 {
1077 return Fail("%s: Could not read output 0", __func__);
1078 }
1079
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001080 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001081 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001082 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001083 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001084 }
1085
1086 bool isSupported = false;
1087 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1088 IsMinimumSupported,
1089 data.m_Backends,
1090 isSupported,
1091 input0.GetTensorInfo(),
1092 input1.GetTensorInfo(),
1093 outputInfo);
1094
1095 if (!isSupported)
1096 {
1097 return false;
1098 }
1099
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001100 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001101 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001102 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1103 if (!isReshapeSupported)
1104 {
1105 return false;
1106 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001107
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001108 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001109}
1110
Mike Kelly46272802019-08-14 17:00:48 +01001111bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1112{
1113 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1114 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1115}
1116
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001117bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1118{
1119 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1120 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1121}
1122
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001123bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1124{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001125 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1126
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001127 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1128 if (!input.IsValid())
1129 {
1130 return Fail("%s: Could not read input 0", __func__);
1131 }
1132
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001133 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1134 if (!output)
1135 {
1136 return Fail("%s: Could not read output", __func__);
1137 }
1138
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001139 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001140 unsigned int rank = inputInfo.GetNumDimensions();
1141
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001142 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001143 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1144 {
1145 return Fail("%s: Could not convert paddings", __func__);
1146 }
1147
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001148 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001149 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001150 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001151 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001152 }
1153
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001154 // Determine type of padding value
1155 OperandType operandType0;
1156 OperandType operandType2;
1157
1158 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1159 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1160 {
1161 return Fail("%s: Operation has invalid inputs", __func__);
1162 }
1163
1164 // Read value to use for padding
1165 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1166 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001167 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001168 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1169 {
1170 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1171 }
1172
1173 descriptor.m_PadValue = f16PadValue;
1174 }
1175 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1176 {
1177 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1178 {
1179 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1180 }
1181 }
1182 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1183 {
Mike Kelly3c673942019-07-25 09:26:06 +01001184 int32_t intPadValue = 0;
1185 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001186 {
1187 return Fail("%s: Could not read input 2 (INT32)", __func__);
1188 }
Mike Kelly3c673942019-07-25 09:26:06 +01001189 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001190 }
1191 else
1192 {
1193 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1194 }
1195
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001196 bool isSupported = false;
1197 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1198 IsPadSupported,
1199 data.m_Backends,
1200 isSupported,
1201 inputInfo,
1202 outputInfo,
1203 descriptor);
1204 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001205 {
1206 return false;
1207 }
1208
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001209 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001210 assert(layer != nullptr);
1211 input.Connect(layer->GetInputSlot(0));
1212 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1213
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001214 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001215}
1216
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001217bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1218{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001219 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1220
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001221 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1222 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1223
1224 if (!input.IsValid() || !alpha.IsValid())
1225 {
1226 return Fail("%s: Operation has invalid inputs", __func__);
1227 }
1228
1229 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1230
1231 if (!output)
1232 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001233 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001234 }
1235
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001236 const TensorInfo& inputInfo = input.GetTensorInfo();
1237 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1238 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001239
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001240 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001241 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001242 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001243 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001244
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001245 bool isSupported = false;
1246 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1247 IsPreluSupported,
1248 data.m_Backends,
1249 isSupported,
1250 inputInfo,
1251 alphaInfo,
1252 outputInfo);
1253 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001254 {
1255 return false;
1256 }
1257
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001258 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001259
1260 if (!layer)
1261 {
1262 return Fail("%s: AddPreluLayer failed", __func__);
1263 }
1264
Sadik Armagan64b19b52019-08-19 09:49:58 +01001265 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1266 if (!isReshapeSupported)
1267 {
1268 return false;
1269 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001270
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001271 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001272}
1273
Sadik Armagan5a476a82019-07-30 09:43:18 +01001274bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1275{
1276 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1277
1278 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1279 if (!input.IsValid())
1280 {
1281 return Fail("%s: Operation has invalid input", __func__);
1282 }
1283
1284 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1285 if (!outputOperand)
1286 {
1287 return Fail("%s: Operation has invalid outputs", __func__);
1288 }
1289
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001290 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001291 if (IsDynamicTensor(outputInfo))
1292 {
1293 return Fail("%s: Dynamic output tensors are not supported", __func__);
1294 }
1295
1296 bool isSupported = false;
1297 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1298 IsQuantizeSupported,
1299 data.m_Backends,
1300 isSupported,
1301 input.GetTensorInfo(),
1302 outputInfo);
1303 if (!isSupported)
1304 {
1305 return false;
1306 }
1307
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001308 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001309 assert(layer != nullptr);
1310 input.Connect(layer->GetInputSlot(0));
1311
1312 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1313}
1314
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001315bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1316{
1317 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1318
1319 //Inputs:
1320 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1321 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1322 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1323 if (!input.IsValid())
1324 {
1325 return Fail("%s: Could not read input 0: input", __func__);
1326 }
1327
1328 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1329 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1330 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1331 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1332 if (!previousCellStateIn.IsValid())
1333 {
1334 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1335 }
1336
1337 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1338 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1339 // is quantized with a fixed quantization range of -1, 127/128.
1340 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1341 if (!previousOutputIn.IsValid())
1342 {
1343 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1344 }
1345
1346 // Get the input tensors:
1347 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1348 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1349 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1350 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001351 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001352
1353 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1354 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1355 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1356 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001357 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001358
1359 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1360 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1361 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1362 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001363 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001364
1365 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1366 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1367 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1368 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001369 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001370
1371 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1372 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1373 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1374 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001375 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001376
1377 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1378 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1379 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1380 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001381 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001382
1383 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1384 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1385 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1386 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001387 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001388
1389 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1390 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1391 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1392 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001393 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001394
1395 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1396 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1397 // of input and weights scales and zeroPoint equal to 0.
1398 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001399 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001400
1401 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1402 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1403 // of input and weights scales and zeroPoint equal to 0.
1404 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001405 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001406
1407 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1408 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1409 // and weights scales and zeroPoint equal to 0.
1410 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001411 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001412
1413 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1414 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1415 // of input and weights scales and zeroPoint equal to 0.
1416 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001417 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001418
1419 if (!inputToInputWeightsPin.IsValid() ||
1420 !inputToForgetWeightsPin.IsValid() ||
1421 !inputToCellWeightsPin.IsValid() ||
1422 !inputToOutputWeightsPin.IsValid() ||
1423 !recurrentToInputWeightsPin.IsValid() ||
1424 !recurrentToForgetWeightsPin.IsValid() ||
1425 !recurrentToCellWeightsPin.IsValid() ||
1426 !recurrentToOutputWeightsPin.IsValid() ||
1427 !inputGateBiasPin.IsValid() ||
1428 !forgetGateBiasPin.IsValid() ||
1429 !cellBiasPin.IsValid() ||
1430 !outputGateBiasPin.IsValid())
1431 {
1432 return Fail("%s: Operation has invalid tensor inputs", __func__);
1433 }
1434
1435 // Outputs:
1436 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1437 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1438 // of -2^4, 2^4 * 32767/32768.
1439 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1440 if (!cellStateOut)
1441 {
1442 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1443 }
1444
1445 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1446 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1447 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1448 if (!output)
1449 {
1450 return Fail("%s: Could not read output 1: output", __func__);
1451 }
1452
1453 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001454 const TensorInfo& inputInfo = input.GetTensorInfo();
1455 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1456 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001457
1458 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001459 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1460 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001461
1462 // Dynamic tensors currently not supported
1463 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1464 {
1465 return Fail("%s: Dynamic output tensors are not supported", __func__);
1466 }
1467
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001468 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001469
1470 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1471 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1472 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1473 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1474 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1475 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1476 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1477 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1478 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1479 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1480 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1481 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1482
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001483 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001484 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1485 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1486 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1487 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1488 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1489 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1490 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1491 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1492 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1493 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1494 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1495 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1496
1497 bool isSupported = false;
1498 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1499 IsQuantizedLstmSupported,
1500 data.m_Backends,
1501 isSupported,
1502 inputInfo,
1503 previousCellStateInInfo,
1504 previousOutputInInfo,
1505 cellStateOutInfo,
1506 outputInfo,
1507 paramsInfo);
1508
1509 if (!isSupported)
1510 {
1511 return false;
1512 }
1513
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001514 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001515 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001516 previousCellStateIn.Connect(layer->GetInputSlot(1));
1517 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001518
1519 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1520 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1521}
1522
Sadik Armagan61113162019-07-25 09:09:40 +01001523bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1524{
1525 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1526 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1527}
1528
1529bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1530{
1531 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1532 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1533}
1534
1535bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1536{
1537 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1538 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1539}
1540
Mike Kelly46272802019-08-14 17:00:48 +01001541bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1542{
1543 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1544 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1545}
1546
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001547bool HalPolicy::ConvertResize(const Operation& operation,
1548 const Model& model,
1549 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001550 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001551{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001552 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
1553
1554 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001555 if (!input.IsValid())
1556 {
1557 return Fail("%s: Could not read input 0", __func__);
1558 }
1559
1560 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1561 if (!output)
1562 {
1563 return Fail("%s: Could not read output 0", __func__);
1564 }
1565
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001566 const TensorInfo& inputInfo = input.GetTensorInfo();
1567 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001568
1569 if (IsDynamicTensor(outputInfo))
1570 {
1571 return Fail("%s: Dynamic output tensors are not supported", __func__);
1572 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001573
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001574 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001575 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001576 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1577
1578 OperandType operandType1;
1579 OperandType operandType2;
1580
1581 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1582 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1583 {
1584 return Fail("%s: Operation has invalid inputs", __func__);
1585 }
1586
1587 if (operandType1 != operandType2)
1588 {
1589 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1590 }
1591
1592 if (operandType1 == OperandType::INT32)
1593 {
1594 // Case 1: resizing by shape
1595 int32_t targetWidth = 0;
1596 int32_t targetHeight = 0;
1597
1598 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1599 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1600 {
1601 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1602 }
1603
1604 if (targetWidth < 0 || targetHeight < 0)
1605 {
1606 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1607 "Target width/height cannot be < 0", __func__);
1608 }
1609
1610 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001611 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001612 }
1613 else if (operandType1 == OperandType::FLOAT32)
1614 {
1615 // Case 2: resizing by scale
1616 float widthScale = 1.0f;
1617 float heightScale = 1.0f;
1618
1619 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1620 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1621 {
1622 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1623 }
1624
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001625 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001626 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1627
1628 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1629 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1630
1631 descriptor.m_TargetWidth = std::floor(width * widthScale);
1632 descriptor.m_TargetHeight = std::floor(height * heightScale);
1633 }
1634 else
1635 {
1636 // NOTE: FLOAT16 scales are not supported
1637 return false;
1638 }
1639
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001640 bool isSupported = false;
1641 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1642 IsResizeSupported,
1643 data.m_Backends,
1644 isSupported,
1645 inputInfo,
1646 outputInfo,
1647 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001648
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001649 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001650 {
1651 return false;
1652 }
1653
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001654 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001655
1656 assert(layer != nullptr);
1657
1658 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1659 input.Connect(layer->GetInputSlot(0));
1660
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001661 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001662}
1663
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001664bool HalPolicy::ConvertRsqrt(const Operation& operation, const Model& model, ConversionData& data)
1665{
1666 ALOGV("hal_1_2::HalPolicy::ConvertRsqrt()");
1667
1668 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1669 if (!input.IsValid())
1670 {
1671 return Fail("%s: Operation has invalid input", __func__);
1672 }
1673
1674 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1675 if (!output)
1676 {
1677 return Fail("%s: Could not read output 0", __func__);
1678 }
1679
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001680 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001681 if (IsDynamicTensor(outputInfo))
1682 {
1683 return Fail("%s: Dynamic output tensors are not supported", __func__);
1684 }
1685
1686 bool isSupported = false;
1687 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1688 IsRsqrtSupported,
1689 data.m_Backends,
1690 isSupported,
1691 input.GetTensorInfo(),
1692 outputInfo);
1693
1694 if (!isSupported)
1695 {
1696 return false;
1697 }
1698
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001699 IConnectableLayer* const layer = data.m_Network->AddRsqrtLayer();
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001700 assert(layer != nullptr);
1701 input.Connect(layer->GetInputSlot(0));
1702
1703 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1704}
1705
Finn Williamsd74c5052019-07-30 17:06:00 +01001706bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1707{
1708 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1709 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1710}
1711
Keith Davisa6bc52f2019-06-26 09:39:49 +01001712bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1713{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001714 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001715
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001716 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001717 if (!input.IsValid() )
1718 {
1719 return Fail("%s: Operation has invalid inputs", __func__);
1720 }
1721
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001722 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001723 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001724 if (rank != 4)
1725 {
1726 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1727 }
1728
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001729 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1730 if (!output)
1731 {
1732 return Fail("%s: Could not read output 0", __func__);
1733 }
1734
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001735 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001736 if (IsDynamicTensor(outputInfo))
1737 {
1738 return Fail("%s: Dynamic output tensors are not supported", __func__);
1739 }
1740
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001741 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01001742
1743 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1744
1745 if (desc.m_BlockSize <= 1)
1746 {
1747 return Fail("%s: Block size must be at least 1 in all dimensions");
1748 }
1749
1750 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1751
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001752 bool isSupported = false;
1753 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1754 IsSpaceToDepthSupported,
1755 data.m_Backends,
1756 isSupported,
1757 inputInfo,
1758 outputInfo,
1759 desc);
1760 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01001761 {
1762 return false;
1763 }
1764
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001765 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001766 assert(layer != nullptr);
1767 input.Connect(layer->GetInputSlot(0));
1768
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001769 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001770}
1771
Francis Murtagh074c25a2019-07-22 16:40:57 +01001772bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1773{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001774 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1775
Francis Murtagh074c25a2019-07-22 16:40:57 +01001776 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1777 if (!input.IsValid())
1778 {
1779 return Fail("%s: Operation has invalid inputs", __func__);
1780 }
1781
1782 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1783 if (!outputOperand)
1784 {
1785 return Fail("%s: Operation has no outputs", __func__);
1786 }
1787
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001788 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001789 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001790 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001791 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001792 }
1793
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001794 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01001795 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1796 {
1797 return Fail("%s: Operation has invalid inputs", __func__);
1798 }
1799
1800 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1801 2,
1802 HalPolicy::OperandType::INT32,
1803 desc.m_Axis,
1804 model,
1805 data))
1806 {
1807 return Fail("%s: Operation has invalid inputs", __func__);
1808 }
1809
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01001810 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1811 !(desc.m_Axis == 1 ||
1812 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1813 {
1814 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1815 }
1816
Francis Murtagh074c25a2019-07-22 16:40:57 +01001817 bool isSupported = false;
1818 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1819 IsSoftmaxSupported,
1820 data.m_Backends,
1821 isSupported,
1822 input.GetTensorInfo(),
1823 outputInfo,
1824 desc);
1825 if (!isSupported)
1826 {
1827 return false;
1828 }
1829
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001830 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001831 assert(layer != nullptr);
1832 input.Connect(layer->GetInputSlot(0));
1833
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001834 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001835}
1836
Mike Kelly0a879362019-07-29 16:56:31 +01001837bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
1838{
1839 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
1840 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
1841}
1842
Sadik Armagan61113162019-07-25 09:09:40 +01001843bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1844{
1845 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
1846 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
1847}
1848
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001849bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
1850{
1851 // Inputs:
1852 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
1853 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
1854 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1855 if (!input.IsValid())
1856 {
1857 return Fail("%s: Could not read input 0: input", __func__);
1858 }
1859 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1860 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
1861 if (!outputStateIn.IsValid())
1862 {
1863 return Fail("%s: Could not read input 18: outputStateIn", __func__);
1864 }
1865 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1866 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
1867 if (!cellStateIn.IsValid())
1868 {
1869 return Fail("%s: Could not read input 19: cellStateIn", __func__);
1870 }
1871
1872 // Get the mandatory input tensors:
1873 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1874 // [num_units, input_size].
1875 const ConstTensorPin inputToForgetWeightsPin =
1876 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1877 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1878 // [num_units, input_size].
1879 const ConstTensorPin inputToCellWeightsPin =
1880 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
1881 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1882 // [num_units, input_size].
1883 const ConstTensorPin inputToOutputWeightsPin =
1884 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
1885 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1886 // [num_units, output_size].
1887 const ConstTensorPin recurrentToForgetWeightsPin =
1888 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
1889 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1890 // [num_units, output_size].
1891 const ConstTensorPin recurrentToCellWeightsPin =
1892 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
1893 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1894 // [num_units, output_size].
1895 const ConstTensorPin recurrentToOutputWeightsPin =
1896 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
1897 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1898 const ConstTensorPin forgetGateBiasPin =
1899 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
1900 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1901 const ConstTensorPin cellBiasPin =
1902 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
1903 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1904 const ConstTensorPin outputGateBiasPin =
1905 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
1906
1907 if (!inputToForgetWeightsPin.IsValid() ||
1908 !inputToCellWeightsPin.IsValid() ||
1909 !inputToOutputWeightsPin.IsValid() ||
1910 !recurrentToForgetWeightsPin.IsValid() ||
1911 !recurrentToCellWeightsPin.IsValid() ||
1912 !recurrentToOutputWeightsPin.IsValid() ||
1913 !forgetGateBiasPin.IsValid() ||
1914 !cellBiasPin.IsValid() ||
1915 !outputGateBiasPin.IsValid())
1916 {
1917 return Fail("%s: Operation has invalid tensor inputs", __func__);
1918 }
1919
1920 // Get the optional input tensors:
1921 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1922 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
1923 const ConstTensorPin inputToInputWeightsPin =
1924 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1925 1,
1926 model,
1927 data,
1928 g_DontPermute,
1929 nullptr,
1930 true);
1931
1932 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1933 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
1934 // “num_units”), or the second dimension of the “projection_weights”, if defined.
1935 const ConstTensorPin recurrentToInputWeightsPin =
1936 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1937 5,
1938 model,
1939 data,
1940 g_DontPermute,
1941 nullptr,
1942 true);
1943
1944 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1945 const ConstTensorPin cellToInputWeightsPin =
1946 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1947 9,
1948 model,
1949 data,
1950 g_DontPermute,
1951 nullptr,
1952 true);
1953
1954 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1955 const ConstTensorPin cellToForgetWeightsPin =
1956 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1957 10,
1958 model,
1959 data,
1960 g_DontPermute,
1961 nullptr,
1962 true);
1963
1964 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1965 const ConstTensorPin cellToOutputWeightsPin =
1966 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1967 11,
1968 model,
1969 data,
1970 g_DontPermute,
1971 nullptr,
1972 true);
1973
1974 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1975 const ConstTensorPin inputGateBiasPin =
1976 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1977 12,
1978 model,
1979 data,
1980 g_DontPermute,
1981 nullptr,
1982 true);
1983
1984 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1985 // [output_size, num_units].
1986 const ConstTensorPin projectionWeightsPin =
1987 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1988 16,
1989 model,
1990 data,
1991 g_DontPermute,
1992 nullptr,
1993 true);
1994
1995 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
1996 const ConstTensorPin projectionBiasPin =
1997 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1998 17,
1999 model,
2000 data,
2001 g_DontPermute,
2002 nullptr,
2003 true);
2004
2005 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2006 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2007 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2008 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2009 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2010 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2011 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2012 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2013 {
2014 return Fail("%s: Operation has invalid tensor inputs", __func__);
2015 }
2016
2017 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2018 // 20: The activation function: A value indicating the activation function:
2019 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2020 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2021 // If set to 0.0 then clipping is disabled.
2022 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2023 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2024 ActivationFn activation;
2025 float cellClip;
2026 float projClip;
2027 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2028 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2029 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2030 {
2031 return Fail("%s: Operation has invalid scalar inputs", __func__);
2032 }
2033
2034 // Get the normalization tensors
2035 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2036 // Used to rescale normalized inputs to activation at input gate.
2037 const ConstTensorPin inputLayerNormWeightsPin =
2038 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2039 23,
2040 model,
2041 data,
2042 g_DontPermute,
2043 nullptr,
2044 true);
2045
2046 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2047 // Used to rescale normalized inputs to activation at forget gate.
2048 const ConstTensorPin forgetLayerNormWeightsPin =
2049 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2050 24,
2051 model,
2052 data,
2053 g_DontPermute,
2054 nullptr,
2055 true);
2056
2057 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2058 // Used to rescale normalized inputs to activation at cell gate.
2059 const ConstTensorPin cellLayerNormWeightsPin =
2060 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2061 25,
2062 model,
2063 data,
2064 g_DontPermute,
2065 nullptr,
2066 true);
2067
2068 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2069 // Used to rescale normalized inputs to activation at output gate.
2070 const ConstTensorPin outputLayerNormWeightsPin =
2071 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2072 26,
2073 model,
2074 data,
2075 g_DontPermute,
2076 nullptr,
2077 true);
2078
2079 // Outputs:
2080 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2081 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2082 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2083 if (!scratchBuffer)
2084 {
2085 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2086 }
2087 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2088 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2089 if (!outputStateOut)
2090 {
2091 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2092 }
2093 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2094 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2095 if (!cellStateOut)
2096 {
2097 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2098 }
2099 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2100 // effectively the same as the current “output state (out)” value.
2101 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2102 if (!output)
2103 {
2104 return Fail("%s: Could not read output 3: output", __func__);
2105 }
2106
2107 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002108 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002109 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2110 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2111 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2112 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2113 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2114 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2115 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2116 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2117 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2118 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2119 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2120 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2121 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2122 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2123 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2124 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2125 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2126 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2127 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2128 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2129 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2130
2131 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002132 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002133 desc.m_ActivationFunc = activation;
2134 desc.m_ClippingThresCell = cellClip;
2135 desc.m_ClippingThresProj = projClip;
2136 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2137 params.m_RecurrentToInputWeights == nullptr ||
2138 params.m_InputGateBias == nullptr);
2139 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2140 params.m_CellToOutputWeights != nullptr);
2141 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2142 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2143 params.m_ForgetLayerNormWeights != nullptr ||
2144 params.m_CellLayerNormWeights != nullptr ||
2145 params.m_OutputLayerNormWeights != nullptr);
2146
2147 // validate the optional input groups
2148 if (desc.m_CifgEnabled &&
2149 (params.m_InputToInputWeights != nullptr ||
2150 params.m_RecurrentToInputWeights != nullptr ||
2151 params.m_InputGateBias != nullptr))
2152 {
2153 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2154 " and input gate bias must be provided", __func__);
2155 }
2156
2157 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2158 {
2159 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2160 }
2161
2162 if (desc.m_PeepholeEnabled &&
2163 (params.m_CellToForgetWeights == nullptr ||
2164 params.m_CellToOutputWeights == nullptr ||
2165 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2166 {
2167 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2168 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2169 }
2170
2171 if (desc.m_LayerNormEnabled &&
2172 (params.m_ForgetLayerNormWeights == nullptr ||
2173 params.m_CellLayerNormWeights == nullptr ||
2174 params.m_OutputLayerNormWeights == nullptr ||
2175 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2176 {
2177 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2178 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2179 }
2180
2181 // Check if the layer is supported
2182 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002183 const TensorInfo& inputInfo = input.GetTensorInfo();
2184 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2185 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002186
2187 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002188 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2189 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2190 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2191 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002192
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002193 if (IsDynamicTensor(scratchBufferInfo) ||
2194 IsDynamicTensor(outputStateOutInfo) ||
2195 IsDynamicTensor(cellStateOutInfo) ||
2196 IsDynamicTensor(outputInfo))
2197 {
2198 return Fail("%s: Dynamic output tensors are not supported", __func__);
2199 }
2200
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002201 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002202 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002203 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2204 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2205 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2206 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2207 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2208 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2209 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2210 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2211 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2212
2213 // Optional parameters
2214 if(!desc.m_CifgEnabled)
2215 {
2216 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2217 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2218 if (params.m_CellToInputWeights != nullptr)
2219 {
2220 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2221 }
2222 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2223 }
2224
2225 if(desc.m_ProjectionEnabled)
2226 {
2227 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2228 if (params.m_ProjectionBias != nullptr)
2229 {
2230 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2231 }
2232 }
2233
2234 if(desc.m_PeepholeEnabled)
2235 {
2236 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2237 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2238 }
2239
2240 if (desc.m_LayerNormEnabled)
2241 {
2242 if(!desc.m_CifgEnabled)
2243 {
2244 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2245 }
2246 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2247 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2248 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2249 }
2250
2251 bool isSupported = false;
2252 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2253 IsLstmSupported,
2254 data.m_Backends,
2255 isSupported,
2256 inputInfo,
2257 outputStateInInfo,
2258 cellStateInInfo,
2259 scratchBufferInfo,
2260 outputStateOutInfo,
2261 cellStateOutInfo,
2262 outputInfo,
2263 desc,
2264 paramsInfo);
2265 if (!isSupported)
2266 {
2267 return false;
2268 }
2269
2270 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002271 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002272
2273 input.Connect(layer->GetInputSlot(0));
2274 outputStateIn.Connect(layer->GetInputSlot(1));
2275 cellStateIn.Connect(layer->GetInputSlot(2));
2276
2277 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
2278 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2279 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2280 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2281}
2282
Sadik Armagan701d9a02019-09-04 15:16:18 +01002283bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2284{
2285 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002286 ActivationDescriptor desc;
2287 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002288
2289 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2290}
2291
Mike Kelly46272802019-08-14 17:00:48 +01002292bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2293{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002294 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002295 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2296}
2297
2298bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2299{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002300 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002301 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2302}
2303
2304bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2305{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002306 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002307 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2308}
2309
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002310bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002311{
2312 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2313
2314 if (!input.IsValid())
2315 {
2316 return Fail("%s: Operation has invalid inputs", __func__);
2317 }
2318
2319 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2320
2321 if (!output)
2322 {
2323 return Fail("%s: Could not read output 0", __func__);
2324 }
2325
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002326 const TensorInfo& inputInfo = input.GetTensorInfo();
2327 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002328 if (IsDynamicTensor(outputInfo))
2329 {
2330 return Fail("%s: Dynamic output tensors are not supported", __func__);
2331 }
2332
2333 // ArmNN does not currently support non-fixed weights or bias
2334 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2335 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2336
2337 if (weightsOperand == nullptr)
2338 {
2339 return Fail("%s: Operand is invalid", __func__);
2340 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002341 TransposeConvolution2dDescriptor desc;
2342 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002343
2344 // Determine whether padding is implicit or explicit
2345 bool implicitPadding = operation.inputs.size() == 9;
2346
2347 if (implicitPadding )
2348 {
2349 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2350 }
2351 else
2352 {
2353 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2354 }
2355
2356 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2357 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2358 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2359
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002360 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002361
2362 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2363 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002364 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002365 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2366 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2367
2368 // Bias is a 1D tensor
2369 const ConstTensorPin biasPin =
2370 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2371
2372 if (!weightsPin.IsValid())
2373 {
2374 return Fail("%s: Operation has invalid weights", __func__);
2375 }
2376
2377 if (!biasPin.IsValid())
2378 {
2379 return Fail("%s: Operation has invalid biases", __func__);
2380 }
2381
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002382 ConstTensor weights = weightsPin.GetConstTensor();
2383 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002384 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2385
2386 ActivationFn activation;
2387
2388 if (implicitPadding)
2389 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002390 int32_t strideX{0};
2391 int32_t strideY{0};
2392 int32_t padLeft{0};
2393 int32_t padRight{0};
2394 int32_t padTop{0};
2395 int32_t padBottom{0};
2396
David Monahan613b49c2019-06-27 11:37:47 +01002397 android::nn::PaddingScheme paddingScheme;
2398 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002399 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2400 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002401 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2402 {
2403 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2404 }
2405
2406 const uint32_t kernelX = weights.GetShape()[widthIndex];
2407 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002408 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2409 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002410
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002411 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2412 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2413
2414 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2415 // but Arm NN only supports values >= 0
2416 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2417 {
2418 return Fail("%s: Negative padding values are not supported", __func__);
2419 }
2420
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002421 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2422 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002423 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2424 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2425 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2426 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002427 }
2428 else if (operation.inputs.size() == 11)
2429 {
2430 // explicit padding
2431 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2432 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2433 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2434 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2435 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2436 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2437 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2438 {
2439 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2440 }
2441 }
2442 else
2443 {
2444 return Fail("%s: Unsupported number of operation inputs", __func__);
2445 }
2446
2447 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002448 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002449
2450 bool isSupported = false;
2451 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2452 IsTransposeConvolution2dSupported,
2453 data.m_Backends,
2454 isSupported,
2455 inputInfo,
2456 outputInfo,
2457 desc,
2458 weights.GetInfo(),
2459 biases);
2460 if (!isSupported)
2461 {
2462 return false;
2463 }
2464
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002465 IConnectableLayer* startLayer =
2466 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002467 if (!startLayer)
2468 {
2469 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2470 }
2471
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002472 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002473 if (!endLayer)
2474 {
2475 return Fail("%s: ProcessActivation failed", __func__);
2476 }
2477
2478 input.Connect(startLayer->GetInputSlot(0));
2479
2480 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2481}
2482
Mike Kellyb5fdf382019-06-11 16:35:25 +01002483} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002484} // namespace armnn_driver