blob: 12c080473cf125ff37804df6a319a22c444936b4 [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>
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +000017#include <string>
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010018
Mike Kellyb5fdf382019-06-11 16:35:25 +010019namespace armnn_driver
20{
21namespace hal_1_2
22{
23
Teresa Charlin8f6429d2019-10-01 13:10:15 +010024using namespace armnn;
25
Mike Kellyb5fdf382019-06-11 16:35:25 +010026bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
27{
Mike Kellyb5fdf382019-06-11 16:35:25 +010028 switch (operation.type)
29 {
Kevin May407718f2019-09-09 14:46:41 +010030 case V1_2::OperationType::ABS:
31 return ConvertAbs(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010032 case V1_2::OperationType::ADD:
33 return ConvertAdd(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010034 case V1_2::OperationType::AVERAGE_POOL_2D:
35 return ConvertAveragePool2d(operation, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +010036 case V1_2::OperationType::BATCH_TO_SPACE_ND:
37 return ConvertBatchToSpaceNd(operation, model, data);
Mike Kellyb8805202019-07-31 17:25:43 +010038 case V1_2::OperationType::CONCATENATION:
39 return ConvertConcatenation(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010040 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010041 return ConvertConv2d(operation, model, data);
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +010042 case V1_2::OperationType::DEPTH_TO_SPACE:
43 return ConvertDepthToSpace(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010044 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010045 return ConvertDepthwiseConv2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010046 case V1_2::OperationType::DEQUANTIZE:
47 return ConvertDequantize(operation, model, data);
48 case V1_2::OperationType::DIV:
49 return ConvertDiv(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +000050 case V1_2::OperationType::EQUAL:
51 return ConvertComparison(operation, model, data, ComparisonOperation::Equal);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +010052 case V1_2::OperationType::EXPAND_DIMS:
53 return ConvertExpandDims(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010054 case V1_2::OperationType::FLOOR:
55 return ConvertFloor(operation, model, data);
56 case V1_2::OperationType::FULLY_CONNECTED:
57 return ConvertFullyConnected(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +000058 case V1_2::OperationType::GREATER:
59 return ConvertComparison(operation, model, data, ComparisonOperation::Greater);
60 case V1_2::OperationType::GREATER_EQUAL:
61 return ConvertComparison(operation, model, data, ComparisonOperation::GreaterOrEqual);
Teresa Charlin8f6429d2019-10-01 13:10:15 +010062 case V1_2::OperationType::GROUPED_CONV_2D:
63 return ConvertGroupedConv2d(operation, model, data);
Aron Virginas-Tara2a73802019-10-09 15:30:40 +010064 case V1_2::OperationType::INSTANCE_NORMALIZATION:
65 return ConvertInstanceNormalization(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010066 case V1_2::OperationType::L2_NORMALIZATION:
67 return ConvertL2Normalization(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010068 case V1_2::OperationType::L2_POOL_2D:
69 return ConvertL2Pool2d(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +000070 case V1_2::OperationType::LESS:
71 return ConvertComparison(operation, model, data, ComparisonOperation::Less);
72 case V1_2::OperationType::LESS_EQUAL:
73 return ConvertComparison(operation, model, data, ComparisonOperation::LessOrEqual);
Mike Kelly46272802019-08-14 17:00:48 +010074 case V1_2::OperationType::LOCAL_RESPONSE_NORMALIZATION:
75 return ConvertLocalResponseNormalization(operation, model, data);
76 case V1_2::OperationType::LOGISTIC:
77 return ConvertLogistic(operation, model, data);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +010078 case V1_2::OperationType::LOG_SOFTMAX:
79 return ConvertLogSoftmax(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010080 case V1_2::OperationType::LSTM:
81 return ConvertLstm(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010082 case V1_2::OperationType::MAX_POOL_2D:
83 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +010084 case V1_2::OperationType::MAXIMUM:
85 return ConvertMaximum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010086 case V1_2::OperationType::MEAN:
87 return ConvertMean(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +010088 case V1_2::OperationType::MINIMUM:
89 return ConvertMinimum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010090 case V1_2::OperationType::MUL:
91 return ConvertMul(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +000092 case V1_2::OperationType::NOT_EQUAL:
93 return ConvertComparison(operation, model, data, ComparisonOperation::NotEqual);
Mike Kelly3c673942019-07-25 09:26:06 +010094 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +010095 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +010096 case V1_2::OperationType::PAD_V2:
97 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +010098 case V1_2::OperationType::PRELU:
99 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +0100100 case V1_2::OperationType::QUANTIZE:
101 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100102 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
103 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100104 case V1_2::OperationType::RELU:
105 return ConvertReLu(operation, model, data);
106 case V1_2::OperationType::RELU1:
107 return ConvertReLu1(operation, model, data);
108 case V1_2::OperationType::RELU6:
109 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100110 case V1_2::OperationType::RESHAPE:
111 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100112 case V1_2::OperationType::RESIZE_BILINEAR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100113 return ConvertResize(operation, model, data, ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100114 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100115 return ConvertResize(operation, model, data, ResizeMethod::NearestNeighbor);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +0100116 case V1_2::OperationType::RSQRT:
117 return ConvertRsqrt(operation, model, data);
Sadik Armagan701d9a02019-09-04 15:16:18 +0100118 case V1_2::OperationType::SQRT:
119 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100120 case V1_2::OperationType::SQUEEZE:
121 return ConvertSqueeze(operation, model, data);
122 case V1_2::OperationType::STRIDED_SLICE:
123 return ConvertStridedSlice(operation, model, data);
124 case V1_2::OperationType::TRANSPOSE:
125 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +0100126 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +0100127 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +0100128 case V1_2::OperationType::SOFTMAX:
129 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +0100130 case V1_2::OperationType::SPACE_TO_BATCH_ND :
131 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100132 case V1_2::OperationType::SPACE_TO_DEPTH:
133 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100134 case V1_2::OperationType::SUB:
135 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100136 case V1_2::OperationType::TANH:
137 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100138 default:
139 return Fail("%s: Operation type %s not supported in ArmnnDriver",
140 __func__, toString(operation.type).c_str());
141 }
142}
143
Kevin May407718f2019-09-09 14:46:41 +0100144bool HalPolicy::ConvertAbs(const Operation& operation, const Model& model, ConversionData& data)
145{
146 ALOGV("hal_1_2::HalPolicy::ConvertAbs()");
147 return ::ConvertAbs<hal_1_2::HalPolicy>(operation, model, data);
148}
149
Mike Kelly46272802019-08-14 17:00:48 +0100150bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
151{
152 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
153 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
154}
155
Sadik Armagan15d63e22019-07-26 16:59:35 +0100156bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
157{
158 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100159 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Average, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100160}
161
Finn Williams23b87b32019-07-30 11:44:05 +0100162bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
163{
164 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
165 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
166}
167
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000168bool HalPolicy::ConvertComparison(const Operation& operation,
169 const Model& model,
170 ConversionData& data,
171 ComparisonOperation comparisonOperation)
172{
173 ALOGV("hal_1_2::HalPolicy::ConvertComparison()");
174 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
175
176 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
177 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
178
179 if (!(input0.IsValid() && input1.IsValid()))
180 {
181 return Fail("%s: Operation has invalid inputs", __func__);
182 }
183
184 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
185 if (!output)
186 {
187 return Fail("%s: Could not read output 0", __func__);
188 }
189
190 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
191 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
192 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
193
194 if (IsDynamicTensor(outputInfo))
195 {
196 return Fail("%s: Dynamic output tensors are not supported", __func__);
197 }
198
199 ComparisonDescriptor descriptor(comparisonOperation);
200
201 bool isSupported = false;
202 FORWARD_LAYER_SUPPORT_FUNC(__func__,
203 IsComparisonSupported,
204 data.m_Backends,
205 isSupported,
206 inputInfo0,
207 inputInfo1,
208 outputInfo,
209 descriptor);
210
211 if (!isSupported)
212 {
213 return false;
214 }
215
216 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
217 assert(layer != nullptr);
218
219 input0.Connect(layer->GetInputSlot(0));
220 input1.Connect(layer->GetInputSlot(1));
221
222 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
223}
224
Mike Kellyb8805202019-07-31 17:25:43 +0100225bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
226{
227 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
228 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
229}
230
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100231bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
232{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100233 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
234
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100235 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
236 if (!input.IsValid())
237 {
238 return Fail("%s: Operation has invalid inputs", __func__);
239 }
240
241 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
242 if (!output)
243 {
244 return Fail("%s: Could not read output 0", __func__);
245 }
246
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100247 const TensorInfo& inputInfo = input.GetTensorInfo();
248 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100249
250 if (IsDynamicTensor(outputInfo))
251 {
252 return Fail("%s: Dynamic output tensors are not supported", __func__);
253 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100254
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100255 Convolution2dDescriptor desc;
256 desc.m_DataLayout = DataLayout::NHWC;
Mike Kellye1d60bb2019-07-11 11:44:52 +0100257
258 // Determine whether padding is implicit or explicit
259 bool implicitPadding = operation.inputs.size() == 7 ||
260 (operation.inputs.size() >= 8 &&
261 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
262
263 if (implicitPadding)
264 {
265 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
266 }
267 else if (operation.inputs.size() >= 10)
268 {
269 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
270 }
271
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100272 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
Mike Kellye1d60bb2019-07-11 11:44:52 +0100273
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100274 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100275 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
276 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
277 // the DataLayout is NCHW
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100278 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
Mike Kellye1d60bb2019-07-11 11:44:52 +0100279 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
280 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100281 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100282 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100283
284 if (!weightsPin.IsValid())
285 {
286 return Fail("%s: Operation has invalid weights", __func__);
287 }
288
289 if (!biasPin.IsValid())
290 {
291 return Fail("%s: Operation has invalid biases", __func__);
292 }
293
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100294 ConstTensor weights = weightsPin.GetConstTensor();
295 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100296 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
297
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100298 ActivationFn activation;
299
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100300 if (implicitPadding)
301 {
302 android::nn::PaddingScheme paddingScheme;
303 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
304 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
305 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
306 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
307 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
308 {
309 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
310 }
311
Mike Kellye1d60bb2019-07-11 11:44:52 +0100312 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
313 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
314 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
315 const uint32_t kernelX = weights.GetShape()[widthIndex];
316 const uint32_t kernelY = weights.GetShape()[heightIndex];
317 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
318 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100319
Mike Kelly86b36d42019-07-12 16:39:33 +0100320 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
321 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100322
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100323 }
324 else if (operation.inputs.size() >= 10)
325 {
326 // explicit padding
327 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
328 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
329 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
330 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
331 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
332 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
333 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
334 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
335 {
336 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
337 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100338 }
339 else
340 {
341 return Fail("%s: Unsupported number of operation inputs", __func__);
342 }
343
344 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100345 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100346
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100347 bool isSupported = false;
348 FORWARD_LAYER_SUPPORT_FUNC(__func__,
349 IsConvolution2dSupported,
350 data.m_Backends,
351 isSupported,
352 inputInfo,
353 outputInfo,
354 desc,
355 weights.GetInfo(),
356 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100357
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100358 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100359 {
360 return false;
361 }
362
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100363 IConnectableLayer* startLayer =
364 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100365
366 if (!startLayer)
367 {
368 return Fail("%s: AddConvolution2dLayer failed", __func__);
369 }
370
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100371 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100372
373 if (!endLayer)
374 {
375 return Fail("%s: ProcessActivation failed", __func__);
376 }
377
378 input.Connect(startLayer->GetInputSlot(0));
379
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100380 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100381}
382
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100383bool HalPolicy::ConvertDepthToSpace(const Operation& operation, const Model& model, ConversionData& data)
384{
385 ALOGV("hal_1_2::HalPolicy::ConvertDepthToSpace()");
386 return ::ConvertDepthToSpace<hal_1_2::HalPolicy>(operation, model, data);
387}
388
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100389bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
390{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100391 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
392
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100393 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
394
395 if (!input.IsValid())
396 {
397 return Fail("%s: Operation has invalid inputs", __func__);
398 }
399
400 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
401
402 if (!output)
403 {
404 return Fail("%s: Could not read output 0", __func__);
405 }
406
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100407 const TensorInfo& inputInfo = input.GetTensorInfo();
408 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100409
410 if (IsDynamicTensor(outputInfo))
411 {
412 return Fail("%s: Dynamic output tensors are not supported", __func__);
413 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100414
415 // ArmNN does not currently support non-fixed weights or bias
416 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
417 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
418
419 if (weightsOperand == nullptr)
420 {
421 return Fail("%s: Operand is invalid", __func__);
422 }
Teresa Charlin3b959602019-10-31 17:05:47 +0000423 if ( weightsOperand->dimensions[0] != 1)
424 {
425 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
426 __func__, weightsOperand->dimensions[0] );
427 }
428
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100429 DepthwiseConvolution2dDescriptor desc;
430 desc.m_DataLayout = DataLayout::NHWC;
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100431
432 // Determine whether padding is implicit or explicit
433 bool implicitPadding = operation.inputs.size() == 8 ||
434 (operation.inputs.size() >= 9 &&
435 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
436
437 // Look ahead to find the optional DataLayout, if present
438 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
439 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
440
441 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
442 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
443 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
444 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
445
446 // Reinterpret weight data as [ H, W, I, M ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100447 TensorShape weightsShape({ weightsOperand->dimensions[1],
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100448 weightsOperand->dimensions[2],
449 inputInfo.GetShape()[channelsIndex],
450 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
451
452 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100453 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100454
455 const ConstTensorPin weightsPin =
456 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
457 1,
458 model,
459 data,
460 HWIMToMIHW,
461 &weightsShape);
462
463 // Bias is a 1D tensor
464 const ConstTensorPin biasPin =
465 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
466
467 if (!weightsPin.IsValid())
468 {
469 return Fail("%s: Operation has invalid weights", __func__);
470 }
471
472 if (!biasPin.IsValid())
473 {
474 return Fail("%s: Operation has invalid biases", __func__);
475 }
476
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100477 ConstTensor weights = weightsPin.GetConstTensor();
478 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100479 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
480
481 ActivationFn activation;
482
483 if (implicitPadding)
484 {
485 android::nn::PaddingScheme paddingScheme;
486 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
487 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
488 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
489 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
490 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
491 {
492 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
493 }
494
495 const uint32_t kernelX = weights.GetShape()[3];
496 const uint32_t kernelY = weights.GetShape()[2];
497 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
498 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
499
Mike Kelly86b36d42019-07-12 16:39:33 +0100500 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
501 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100502 }
503 else if (operation.inputs.size() >= 11)
504 {
505 // explicit padding
506 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
507 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
508 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
509 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
510 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
511 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
512 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
513 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
514 {
515 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
516 }
517 }
518 else
519 {
520 return Fail("%s: Unsupported number of operation inputs", __func__);
521 }
522
523 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100524 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100525
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100526 bool isSupported = false;
527 FORWARD_LAYER_SUPPORT_FUNC(__func__,
528 IsDepthwiseConvolutionSupported,
529 data.m_Backends,
530 isSupported,
531 inputInfo,
532 outputInfo,
533 desc,
534 weights.GetInfo(),
535 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100536
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100537 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100538 {
539 return false;
540 }
541
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100542 IConnectableLayer* startLayer =
543 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100544
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100545 if (!startLayer)
546 {
547 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
548 }
549
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100550 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100551 if (!endLayer)
552 {
553 return Fail("%s: ProcessActivation failed", __func__);
554 }
555
556 input.Connect(startLayer->GetInputSlot(0));
557
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100558 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100559}
560
Mike Kelly46272802019-08-14 17:00:48 +0100561bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
562{
563 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
564 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
565}
566
567bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
568{
569 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
570 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
571}
572
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100573bool HalPolicy::ConvertExpandDims(const Operation& operation, const Model& model, ConversionData& data)
574{
575 ALOGV("hal_1_2::HalPolicy::ConvertExpandDims()");
576
577 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
578
579 if (!input.IsValid())
580 {
581 return Fail("%s: Operation has invalid input", __func__);
582 }
583
584 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
585 if (!output)
586 {
587 return Fail("%s: Operation has invalid output", __func__);
588 }
589
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100590 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100591 if (IsDynamicTensor(outputInfo))
592 {
593 return Fail("%s: Dynamic output tensors are not supported", __func__);
594 }
595
596 int32_t axis;
597 if (!GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, axis, model, data))
598 {
599 return Fail("%s: failed to get axis input value", __func__);
600 }
601
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100602 TensorShape targetShape;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100603
604 try
605 {
606 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
607 }
608 catch (const std::exception &e)
609 {
610 return Fail("%s: %s", __func__, e.what());
611 }
612
613 if (targetShape != outputInfo.GetShape())
614 {
615 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
616 }
617
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100618 ReshapeDescriptor reshapeDescriptor;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100619 reshapeDescriptor.m_TargetShape = targetShape;
620
621 bool isSupported = false;
622 FORWARD_LAYER_SUPPORT_FUNC(__func__,
623 IsReshapeSupported,
624 data.m_Backends,
625 isSupported,
626 input.GetTensorInfo(),
627 reshapeDescriptor);
628
629 if (!isSupported)
630 {
631 return false;
632 }
633
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100634 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100635 assert(layer != nullptr);
636 input.Connect(layer->GetInputSlot(0));
637
638 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
639}
640
Mike Kelly46272802019-08-14 17:00:48 +0100641bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
642{
643 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
644 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
645}
646
647bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
648{
649 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
650 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
651}
652
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100653bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
654{
655 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
656
657 //
658 // Parse data
659 //
660 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
661 if (!input.IsValid())
662 {
663 return Fail("%s: Operation has invalid inputs", __func__);
664 }
665 const TensorInfo& inputInfo = input.GetTensorInfo();
666
667 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
668 if (!output)
669 {
670 return Fail("%s: Could not read output 0", __func__);
671 }
672 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
673 if (IsDynamicTensor(outputInfo))
674 {
675 return Fail("%s: Dynamic output tensors are not supported", __func__);
676 }
677
678 // Look ahead to determine data layout
679 DataLayout dataLayout = DataLayout::NHWC;
680 if (operation.inputs.size() == 12)
681 {
682 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
683 }
684 else
685 {
686 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
687 }
688
689 // NOTE:
690 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
691 // but Arm NN expects the filter's height and width indices to match the input's height and
692 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
693 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
694 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
695 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
696 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
697 const ConstTensorPin biasesPin =
698 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
699 if (!weightsPin.IsValid() || !biasesPin.IsValid())
700 {
701 return Fail("%s: Operation has invalid inputs", __func__);
702 }
703
704 ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tar4bc42cb2019-11-07 11:12:15 +0000705 if (weights.GetInfo().HasPerAxisQuantization())
706 {
707 return Fail("%s: Per-axis quantization is not supported", __func__);
708 }
709
710 ConstTensor biases = biasesPin.GetConstTensor();
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100711 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
712
713 const TensorShape& inputShape = inputInfo.GetShape();
714 const TensorShape& outputShape = outputInfo.GetShape();
715 const TensorShape& weightsShape = weights.GetShape();
716 const TensorShape& biasesShape = biases.GetShape();
717
718 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
719 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
720 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
721 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
722
723 Convolution2dDescriptor desc;
724 desc.m_DataLayout = dataLayout;
725 desc.m_BiasEnabled = true;
726
727 int numGroups;
728 ActivationFn activation;
729
730 if (operation.inputs.size() == 12)
731 {
732 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
733 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
734 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
735 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
736 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
737 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
738 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
739 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
740 {
741 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
742 }
743
744 }
745 else if (operation.inputs.size() == 9)
746 {
747 android::nn::PaddingScheme paddingScheme;
748 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
749 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
750 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
751 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
752 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
753 {
754 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
755 }
756
757 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
758 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
759
760 const uint32_t kernelX = weightsShape[widthIndex];
761 const uint32_t kernelY = weightsShape[heightIndex];
762
763 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
764 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
765 }
766 else
767 {
768 return Fail("%s: Unsupported number of operation inputs", __func__);
769 }
770
771 const unsigned int outputChannels = outputShape[channelsIndex];
772
773 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
774 const unsigned int channelMultiplier = outputChannels / numGroups;
775
776 //
777 // Validate all relevant inputs
778 //
779 if (numGroups <= 0)
780 {
781 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
782 }
783
784 if (outputChannels % numGroups != 0u)
785 {
786 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
787 }
788
789 //
790 // Set up Splitter layer
791 //
792 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
793 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
794
795 TensorInfo splitterOutputInfo(4,
796 splitterDimSizes,
797 inputInfo.GetDataType(),
798 inputInfo.GetQuantizationScale(),
799 inputInfo.GetQuantizationOffset());
800
801 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
802
803 ViewsDescriptor splitterDesc(numGroups);
804 for (unsigned int group = 0u; group < numGroups; ++group)
805 {
806 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
807 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
808 {
809 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
810 }
811 }
812
813 bool isSupported = false;
814 FORWARD_LAYER_SUPPORT_FUNC(__func__,
815 IsSplitterSupported,
816 data.m_Backends,
817 isSupported,
818 inputInfo,
819 splitterOutputInfos,
820 splitterDesc);
821 if (!isSupported)
822 {
823 return false;
824 }
825
826 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
827 if (!splitterLayer)
828 {
829 return Fail("%s: Failed to add SplitterLayer", __func__);
830 }
831
832 input.Connect(splitterLayer->GetInputSlot(0));
833 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
834 {
835 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
836 }
837
838 //
839 // Set up Convolution2d layers for each group
840 //
841 TensorShape groupInputShape(inputShape);
842 groupInputShape[channelsIndex] = channelsPerGroup;
843
844 TensorShape groupOutputShape(outputShape);
845 groupOutputShape[channelsIndex] = 1;
846
847 TensorShape groupWeightsShape(weightsShape);
848 groupWeightsShape[0] /= channelMultiplier * numGroups;
849
850 TensorShape groupBiasesShape({ 1 });
851
852 const TensorInfo groupInputInfo (groupInputShape,
853 inputInfo.GetDataType(),
854 inputInfo.GetQuantizationScale(),
855 inputInfo.GetQuantizationOffset());
856 const TensorInfo groupWeightsInfo(groupWeightsShape,
857 weights.GetInfo().GetDataType(),
858 weights.GetInfo().GetQuantizationScale(),
859 weights.GetInfo().GetQuantizationOffset());
860 const TensorInfo groupBiasesInfo (groupBiasesShape,
861 biases.GetInfo().GetDataType(),
862 biases.GetInfo().GetQuantizationScale(),
863 biases.GetInfo().GetQuantizationOffset());
864 const TensorInfo groupOutputInfo (groupOutputShape,
865 outputInfo.GetDataType(),
866 outputInfo.GetQuantizationScale(),
867 outputInfo.GetQuantizationOffset());
868
869 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
870 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
871
872 std::vector<IConnectableLayer*> convLayers(numGroups*channelMultiplier, nullptr);
873 for (unsigned int group = 0u; group < numGroups; ++group)
874 {
875 for (unsigned int m = 0u; m < channelMultiplier; ++m)
876 {
877 auto index = group * channelMultiplier + m;
878
879 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
880 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
881
882 // Extract weights and biases data for current group convolution
883 ConstTensor groupWeights(groupWeightsInfo,
884 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
885 weightsDataOffset));
886 ConstTensor groupBiases(groupBiasesInfo,
887 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
888 biasesDataOffset));
889
890 isSupported = false;
891 FORWARD_LAYER_SUPPORT_FUNC(__func__,
892 IsConvolution2dSupported,
893 data.m_Backends,
894 isSupported,
895 groupInputInfo,
896 groupOutputInfo,
897 desc,
898 groupWeightsInfo,
899 Optional<TensorInfo>(groupBiasesInfo));
900 if (!isSupported)
901 {
902 return false;
903 }
904
905 IConnectableLayer *convLayer =
906 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
907 if (!convLayer)
908 {
909 return Fail("%s: AddConvolution2dLayer failed", __func__);
910 }
911
912 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
913 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
914
915 convLayers[index] = convLayer;
916 }
917 }
918
919 //
920 // Set up Concat layer
921 //
922 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
923 for (unsigned int group = 0u; group < numGroups; ++group)
924 {
925 for (unsigned int m = 0u; m < channelMultiplier; ++m)
926 {
927 auto index = group * channelMultiplier + m;
928 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
929 concatDescriptor.SetConcatAxis(channelsIndex);
930 }
931 }
932
933 isSupported = false;
934 FORWARD_LAYER_SUPPORT_FUNC(__func__,
935 IsConcatSupported,
936 data.m_Backends,
937 isSupported,
938 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
939 outputInfo,
940 concatDescriptor);
941 if (!isSupported)
942 {
943 return false;
944 }
945
946 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
947 if (!concatLayer)
948 {
949 return Fail("%s: AddConcatLayer failed", __func__);
950 }
951
952 for (unsigned int group = 0u; group < numGroups; ++group)
953 {
954 for (unsigned int m = 0u; m < channelMultiplier; ++m)
955 {
956 auto index = group * channelMultiplier + m;
957 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
958 }
959 }
960 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
961
962 //
963 // Set up Activation layer (if it is set)
964 //
965 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
966 if (!endLayer)
967 {
968 return Fail("%s: ProcessActivation failed", __func__);
969 }
970
971 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
972}
973
Aron Virginas-Tara2a73802019-10-09 15:30:40 +0100974bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
975{
976 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
977
978 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
979 if (!input.IsValid())
980 {
981 return Fail("%s: Operation has an invalid input 0", __func__);
982 }
983
984 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
985 if (!output)
986 {
987 return Fail("%s: Operation has an invalid output", __func__);
988 }
989
990 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
991 if (IsDynamicTensor(outputInfo))
992 {
993 return Fail("%s: Dynamic output tensors are not supported", __func__);
994 }
995
996 // Determine data type of input tensor
997 OperandType inputType;
998 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
999 {
1000 return Fail("%s: Operation has invalid inputs", __func__);
1001 }
1002
1003 InstanceNormalizationDescriptor desc;
1004
1005 // Read gamma, beta & epsilon
1006 if (inputType == OperandType::TENSOR_FLOAT16)
1007 {
1008 Half fp16Gamma;
1009 Half fp16Beta;
1010 Half fp16Epsilon;
1011
1012 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1013 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1014 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1015 {
1016 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1017 }
1018
1019 desc.m_Gamma = static_cast<float>(fp16Gamma);
1020 desc.m_Beta = static_cast<float>(fp16Beta);
1021 desc.m_Eps = static_cast<float>(fp16Epsilon);
1022 }
1023 else if (inputType == OperandType::TENSOR_FLOAT32)
1024 {
1025 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1026 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1027 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1028 {
1029 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1030 }
1031 }
1032 else
1033 {
1034 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1035 }
1036
1037 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1038
1039 bool isSupported = false;
1040 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1041 IsInstanceNormalizationSupported,
1042 data.m_Backends,
1043 isSupported,
1044 input.GetTensorInfo(),
1045 outputInfo,
1046 desc);
1047 if (!isSupported)
1048 {
1049 return false;
1050 }
1051
1052 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1053 input.Connect(layer->GetInputSlot(0));
1054
1055 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1056}
1057
Mike Kelly46272802019-08-14 17:00:48 +01001058bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1059{
1060 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1061 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1062}
1063
Sadik Armagan15d63e22019-07-26 16:59:35 +01001064bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1065{
1066 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001067 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001068}
1069
Mike Kelly46272802019-08-14 17:00:48 +01001070bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1071 const Model& model,
1072 ConversionData& data)
1073{
1074 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1075 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1076}
1077
1078bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1079{
1080 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1081 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1082}
1083
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001084bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1085{
1086 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1087
1088 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1089 if (!input.IsValid())
1090 {
1091 return Fail("%s: Failed to read input 0", __func__);
1092 }
1093
1094 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1095 if (!output)
1096 {
1097 return Fail("%s: Failed to read output", __func__);
1098 }
1099
1100 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1101 if (IsDynamicTensor(outputInfo))
1102 {
1103 return Fail("%s: Dynamic output tensors are not supported", __func__);
1104 }
1105
1106 // Determine data type of input tensor
1107 OperandType inputType;
1108 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1109 {
1110 return Fail("%s: Operation has invalid inputs", __func__);
1111 }
1112
1113 LogSoftmaxDescriptor descriptor;
1114
1115 // Read beta
1116 if (inputType == OperandType::TENSOR_FLOAT16)
1117 {
1118 Half fp16Beta;
1119 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1120 {
1121 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1122 }
1123
1124 descriptor.m_Beta = static_cast<float>(fp16Beta);
1125 }
1126 else if (inputType == OperandType::TENSOR_FLOAT32)
1127 {
1128 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1129 {
1130 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1131 }
1132 }
1133 else
1134 {
1135 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1136 }
1137
1138 // Read axis
1139 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1140 {
1141 return Fail("%s: Failed to read input 2", __func__);
1142 }
1143
1144 bool isSupported = false;
1145 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1146 IsLogSoftmaxSupported,
1147 data.m_Backends,
1148 isSupported,
1149 input.GetTensorInfo(),
1150 outputInfo,
1151 descriptor);
1152 if (!isSupported)
1153 {
1154 return false;
1155 }
1156
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001157 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001158 if (!layer)
1159 {
1160 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1161 }
1162
1163 input.Connect(layer->GetInputSlot(0));
1164
1165 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1166}
1167
Sadik Armagan15d63e22019-07-26 16:59:35 +01001168bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1169{
1170 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001171 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001172}
1173
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001174bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1175{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001176 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1177
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001178 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1179 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1180
1181 if (!input0.IsValid() || !input1.IsValid())
1182 {
1183 return Fail("%s: Operation has invalid inputs", __func__);
1184 }
1185
1186 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1187 if (!outputOperand)
1188 {
1189 return Fail("%s: Could not read output", __func__);
1190 }
1191
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001192 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001193 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001194 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001195 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001196 }
1197
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001198 bool isSupported = false;
1199 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1200 IsMaximumSupported,
1201 data.m_Backends,
1202 isSupported,
1203 input0.GetTensorInfo(),
1204 input1.GetTensorInfo(),
1205 outInfo);
1206
1207 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001208 {
1209 return false;
1210 }
1211
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001212 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001213 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001214 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1215 if (!isReshapeSupported)
1216 {
1217 return false;
1218 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001219
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001220 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001221}
1222
Mike Kelly46272802019-08-14 17:00:48 +01001223bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1224{
1225 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1226 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1227}
1228
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001229bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1230{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001231 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1232
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001233 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1234 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1235
1236 if (!input0.IsValid() || !input1.IsValid())
1237 {
1238 return Fail("%s: Operation has invalid inputs", __func__);
1239 }
1240
1241 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1242 if (!output)
1243 {
1244 return Fail("%s: Could not read output 0", __func__);
1245 }
1246
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001247 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001248 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001249 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001250 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001251 }
1252
1253 bool isSupported = false;
1254 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1255 IsMinimumSupported,
1256 data.m_Backends,
1257 isSupported,
1258 input0.GetTensorInfo(),
1259 input1.GetTensorInfo(),
1260 outputInfo);
1261
1262 if (!isSupported)
1263 {
1264 return false;
1265 }
1266
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001267 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001268 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001269 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1270 if (!isReshapeSupported)
1271 {
1272 return false;
1273 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001274
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001275 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001276}
1277
Mike Kelly46272802019-08-14 17:00:48 +01001278bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1279{
1280 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1281 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1282}
1283
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001284bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1285{
1286 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1287 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1288}
1289
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001290bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1291{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001292 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1293
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001294 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1295 if (!input.IsValid())
1296 {
1297 return Fail("%s: Could not read input 0", __func__);
1298 }
1299
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001300 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1301 if (!output)
1302 {
1303 return Fail("%s: Could not read output", __func__);
1304 }
1305
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001306 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001307 unsigned int rank = inputInfo.GetNumDimensions();
1308
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001309 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001310 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1311 {
1312 return Fail("%s: Could not convert paddings", __func__);
1313 }
1314
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001315 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001316 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001317 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001318 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001319 }
1320
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001321 // Determine type of padding value
1322 OperandType operandType0;
1323 OperandType operandType2;
1324
1325 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1326 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1327 {
1328 return Fail("%s: Operation has invalid inputs", __func__);
1329 }
1330
1331 // Read value to use for padding
1332 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1333 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001334 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001335 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1336 {
1337 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1338 }
1339
1340 descriptor.m_PadValue = f16PadValue;
1341 }
1342 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1343 {
1344 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1345 {
1346 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1347 }
1348 }
1349 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1350 {
Mike Kelly3c673942019-07-25 09:26:06 +01001351 int32_t intPadValue = 0;
1352 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001353 {
1354 return Fail("%s: Could not read input 2 (INT32)", __func__);
1355 }
Mike Kelly3c673942019-07-25 09:26:06 +01001356 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001357 }
1358 else
1359 {
1360 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1361 }
1362
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001363 bool isSupported = false;
1364 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1365 IsPadSupported,
1366 data.m_Backends,
1367 isSupported,
1368 inputInfo,
1369 outputInfo,
1370 descriptor);
1371 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001372 {
1373 return false;
1374 }
1375
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001376 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001377 assert(layer != nullptr);
1378 input.Connect(layer->GetInputSlot(0));
1379 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1380
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001381 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001382}
1383
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001384bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1385{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001386 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1387
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001388 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1389 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1390
1391 if (!input.IsValid() || !alpha.IsValid())
1392 {
1393 return Fail("%s: Operation has invalid inputs", __func__);
1394 }
1395
1396 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1397
1398 if (!output)
1399 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001400 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001401 }
1402
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001403 const TensorInfo& inputInfo = input.GetTensorInfo();
1404 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1405 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001406
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001407 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001408 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001409 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001410 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001411
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001412 bool isSupported = false;
1413 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1414 IsPreluSupported,
1415 data.m_Backends,
1416 isSupported,
1417 inputInfo,
1418 alphaInfo,
1419 outputInfo);
1420 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001421 {
1422 return false;
1423 }
1424
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001425 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001426
1427 if (!layer)
1428 {
1429 return Fail("%s: AddPreluLayer failed", __func__);
1430 }
1431
Sadik Armagan64b19b52019-08-19 09:49:58 +01001432 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1433 if (!isReshapeSupported)
1434 {
1435 return false;
1436 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001437
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001438 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001439}
1440
Sadik Armagan5a476a82019-07-30 09:43:18 +01001441bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1442{
1443 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1444
1445 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1446 if (!input.IsValid())
1447 {
1448 return Fail("%s: Operation has invalid input", __func__);
1449 }
1450
1451 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1452 if (!outputOperand)
1453 {
1454 return Fail("%s: Operation has invalid outputs", __func__);
1455 }
1456
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001457 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001458 if (IsDynamicTensor(outputInfo))
1459 {
1460 return Fail("%s: Dynamic output tensors are not supported", __func__);
1461 }
1462
1463 bool isSupported = false;
1464 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1465 IsQuantizeSupported,
1466 data.m_Backends,
1467 isSupported,
1468 input.GetTensorInfo(),
1469 outputInfo);
1470 if (!isSupported)
1471 {
1472 return false;
1473 }
1474
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001475 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001476 assert(layer != nullptr);
1477 input.Connect(layer->GetInputSlot(0));
1478
1479 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1480}
1481
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001482bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1483{
1484 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1485
1486 //Inputs:
1487 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1488 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1489 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1490 if (!input.IsValid())
1491 {
1492 return Fail("%s: Could not read input 0: input", __func__);
1493 }
1494
1495 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1496 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1497 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1498 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1499 if (!previousCellStateIn.IsValid())
1500 {
1501 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1502 }
1503
1504 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1505 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1506 // is quantized with a fixed quantization range of -1, 127/128.
1507 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1508 if (!previousOutputIn.IsValid())
1509 {
1510 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1511 }
1512
1513 // Get the input tensors:
1514 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1515 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1516 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1517 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001518 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001519
1520 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1521 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1522 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1523 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001524 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001525
1526 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1527 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1528 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1529 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001530 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001531
1532 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1533 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1534 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1535 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001536 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001537
1538 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1539 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1540 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1541 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001542 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001543
1544 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1545 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1546 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1547 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001548 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001549
1550 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1551 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1552 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1553 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001554 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001555
1556 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1557 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1558 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1559 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001560 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001561
1562 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1563 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1564 // of input and weights scales and zeroPoint equal to 0.
1565 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001566 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001567
1568 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1569 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1570 // of input and weights scales and zeroPoint equal to 0.
1571 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001572 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001573
1574 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1575 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1576 // and weights scales and zeroPoint equal to 0.
1577 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001578 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001579
1580 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1581 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1582 // of input and weights scales and zeroPoint equal to 0.
1583 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001584 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001585
1586 if (!inputToInputWeightsPin.IsValid() ||
1587 !inputToForgetWeightsPin.IsValid() ||
1588 !inputToCellWeightsPin.IsValid() ||
1589 !inputToOutputWeightsPin.IsValid() ||
1590 !recurrentToInputWeightsPin.IsValid() ||
1591 !recurrentToForgetWeightsPin.IsValid() ||
1592 !recurrentToCellWeightsPin.IsValid() ||
1593 !recurrentToOutputWeightsPin.IsValid() ||
1594 !inputGateBiasPin.IsValid() ||
1595 !forgetGateBiasPin.IsValid() ||
1596 !cellBiasPin.IsValid() ||
1597 !outputGateBiasPin.IsValid())
1598 {
1599 return Fail("%s: Operation has invalid tensor inputs", __func__);
1600 }
1601
1602 // Outputs:
1603 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1604 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1605 // of -2^4, 2^4 * 32767/32768.
1606 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1607 if (!cellStateOut)
1608 {
1609 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1610 }
1611
1612 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1613 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1614 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1615 if (!output)
1616 {
1617 return Fail("%s: Could not read output 1: output", __func__);
1618 }
1619
1620 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001621 const TensorInfo& inputInfo = input.GetTensorInfo();
1622 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1623 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001624
1625 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001626 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1627 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001628
1629 // Dynamic tensors currently not supported
1630 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1631 {
1632 return Fail("%s: Dynamic output tensors are not supported", __func__);
1633 }
1634
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001635 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001636
1637 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1638 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1639 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1640 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1641 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1642 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1643 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1644 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1645 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1646 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1647 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1648 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1649
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001650 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001651 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1652 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1653 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1654 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1655 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1656 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1657 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1658 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1659 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1660 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1661 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1662 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1663
1664 bool isSupported = false;
1665 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1666 IsQuantizedLstmSupported,
1667 data.m_Backends,
1668 isSupported,
1669 inputInfo,
1670 previousCellStateInInfo,
1671 previousOutputInInfo,
1672 cellStateOutInfo,
1673 outputInfo,
1674 paramsInfo);
1675
1676 if (!isSupported)
1677 {
1678 return false;
1679 }
1680
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001681 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001682 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001683 previousCellStateIn.Connect(layer->GetInputSlot(1));
1684 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001685
1686 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1687 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1688}
1689
Sadik Armagan61113162019-07-25 09:09:40 +01001690bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1691{
1692 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1693 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1694}
1695
1696bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1697{
1698 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1699 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1700}
1701
1702bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1703{
1704 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1705 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1706}
1707
Mike Kelly46272802019-08-14 17:00:48 +01001708bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1709{
1710 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1711 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1712}
1713
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001714bool HalPolicy::ConvertResize(const Operation& operation,
1715 const Model& model,
1716 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001717 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001718{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001719 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001720 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001721
1722 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001723 if (!input.IsValid())
1724 {
1725 return Fail("%s: Could not read input 0", __func__);
1726 }
1727
1728 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1729 if (!output)
1730 {
1731 return Fail("%s: Could not read output 0", __func__);
1732 }
1733
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001734 const TensorInfo& inputInfo = input.GetTensorInfo();
1735 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001736
1737 if (IsDynamicTensor(outputInfo))
1738 {
1739 return Fail("%s: Dynamic output tensors are not supported", __func__);
1740 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001741
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001742 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001743 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001744 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1745
1746 OperandType operandType1;
1747 OperandType operandType2;
1748
1749 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1750 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1751 {
1752 return Fail("%s: Operation has invalid inputs", __func__);
1753 }
1754
1755 if (operandType1 != operandType2)
1756 {
1757 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1758 }
1759
1760 if (operandType1 == OperandType::INT32)
1761 {
1762 // Case 1: resizing by shape
1763 int32_t targetWidth = 0;
1764 int32_t targetHeight = 0;
1765
1766 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1767 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1768 {
1769 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1770 }
1771
1772 if (targetWidth < 0 || targetHeight < 0)
1773 {
1774 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1775 "Target width/height cannot be < 0", __func__);
1776 }
1777
1778 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001779 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001780 }
1781 else if (operandType1 == OperandType::FLOAT32)
1782 {
1783 // Case 2: resizing by scale
1784 float widthScale = 1.0f;
1785 float heightScale = 1.0f;
1786
1787 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1788 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1789 {
1790 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1791 }
1792
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001793 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001794 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1795
1796 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1797 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1798
1799 descriptor.m_TargetWidth = std::floor(width * widthScale);
1800 descriptor.m_TargetHeight = std::floor(height * heightScale);
1801 }
1802 else
1803 {
1804 // NOTE: FLOAT16 scales are not supported
1805 return false;
1806 }
1807
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001808 bool isSupported = false;
1809 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1810 IsResizeSupported,
1811 data.m_Backends,
1812 isSupported,
1813 inputInfo,
1814 outputInfo,
1815 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001816
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001817 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001818 {
1819 return false;
1820 }
1821
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001822 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001823
1824 assert(layer != nullptr);
1825
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001826 input.Connect(layer->GetInputSlot(0));
1827
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001828 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001829}
1830
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001831bool HalPolicy::ConvertRsqrt(const Operation& operation, const Model& model, ConversionData& data)
1832{
1833 ALOGV("hal_1_2::HalPolicy::ConvertRsqrt()");
1834
1835 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1836 if (!input.IsValid())
1837 {
1838 return Fail("%s: Operation has invalid input", __func__);
1839 }
1840
1841 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1842 if (!output)
1843 {
1844 return Fail("%s: Could not read output 0", __func__);
1845 }
1846
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001847 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001848 if (IsDynamicTensor(outputInfo))
1849 {
1850 return Fail("%s: Dynamic output tensors are not supported", __func__);
1851 }
1852
1853 bool isSupported = false;
1854 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1855 IsRsqrtSupported,
1856 data.m_Backends,
1857 isSupported,
1858 input.GetTensorInfo(),
1859 outputInfo);
1860
1861 if (!isSupported)
1862 {
1863 return false;
1864 }
1865
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001866 IConnectableLayer* const layer = data.m_Network->AddRsqrtLayer();
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001867 assert(layer != nullptr);
1868 input.Connect(layer->GetInputSlot(0));
1869
1870 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1871}
1872
Finn Williamsd74c5052019-07-30 17:06:00 +01001873bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1874{
1875 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1876 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1877}
1878
Keith Davisa6bc52f2019-06-26 09:39:49 +01001879bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1880{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001881 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001882
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001883 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001884 if (!input.IsValid() )
1885 {
1886 return Fail("%s: Operation has invalid inputs", __func__);
1887 }
1888
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001889 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001890 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001891 if (rank != 4)
1892 {
1893 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1894 }
1895
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001896 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1897 if (!output)
1898 {
1899 return Fail("%s: Could not read output 0", __func__);
1900 }
1901
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001902 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001903 if (IsDynamicTensor(outputInfo))
1904 {
1905 return Fail("%s: Dynamic output tensors are not supported", __func__);
1906 }
1907
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001908 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01001909
1910 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1911
1912 if (desc.m_BlockSize <= 1)
1913 {
1914 return Fail("%s: Block size must be at least 1 in all dimensions");
1915 }
1916
1917 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1918
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001919 bool isSupported = false;
1920 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1921 IsSpaceToDepthSupported,
1922 data.m_Backends,
1923 isSupported,
1924 inputInfo,
1925 outputInfo,
1926 desc);
1927 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01001928 {
1929 return false;
1930 }
1931
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001932 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001933 assert(layer != nullptr);
1934 input.Connect(layer->GetInputSlot(0));
1935
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001936 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001937}
1938
Francis Murtagh074c25a2019-07-22 16:40:57 +01001939bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1940{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001941 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1942
Francis Murtagh074c25a2019-07-22 16:40:57 +01001943 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1944 if (!input.IsValid())
1945 {
1946 return Fail("%s: Operation has invalid inputs", __func__);
1947 }
1948
1949 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1950 if (!outputOperand)
1951 {
1952 return Fail("%s: Operation has no outputs", __func__);
1953 }
1954
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001955 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001956 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001957 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001958 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001959 }
1960
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001961 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01001962 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1963 {
1964 return Fail("%s: Operation has invalid inputs", __func__);
1965 }
1966
1967 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1968 2,
1969 HalPolicy::OperandType::INT32,
1970 desc.m_Axis,
1971 model,
1972 data))
1973 {
1974 return Fail("%s: Operation has invalid inputs", __func__);
1975 }
1976
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01001977 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1978 !(desc.m_Axis == 1 ||
1979 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1980 {
1981 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1982 }
1983
Francis Murtagh074c25a2019-07-22 16:40:57 +01001984 bool isSupported = false;
1985 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1986 IsSoftmaxSupported,
1987 data.m_Backends,
1988 isSupported,
1989 input.GetTensorInfo(),
1990 outputInfo,
1991 desc);
1992 if (!isSupported)
1993 {
1994 return false;
1995 }
1996
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001997 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001998 assert(layer != nullptr);
1999 input.Connect(layer->GetInputSlot(0));
2000
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002001 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002002}
2003
Mike Kelly0a879362019-07-29 16:56:31 +01002004bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2005{
2006 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2007 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2008}
2009
Sadik Armagan61113162019-07-25 09:09:40 +01002010bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2011{
2012 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2013 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2014}
2015
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002016bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2017{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002018 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2019
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002020 // Inputs:
2021 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2022 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2023 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2024 if (!input.IsValid())
2025 {
2026 return Fail("%s: Could not read input 0: input", __func__);
2027 }
2028 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2029 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2030 if (!outputStateIn.IsValid())
2031 {
2032 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2033 }
2034 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2035 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2036 if (!cellStateIn.IsValid())
2037 {
2038 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2039 }
2040
2041 // Get the mandatory input tensors:
2042 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2043 // [num_units, input_size].
2044 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002045 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002046 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2047 // [num_units, input_size].
2048 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002049 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002050 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2051 // [num_units, input_size].
2052 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002053 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002054 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2055 // [num_units, output_size].
2056 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002057 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002058 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2059 // [num_units, output_size].
2060 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002061 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002062 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2063 // [num_units, output_size].
2064 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002065 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002066 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2067 const ConstTensorPin forgetGateBiasPin =
2068 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2069 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2070 const ConstTensorPin cellBiasPin =
2071 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2072 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2073 const ConstTensorPin outputGateBiasPin =
2074 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2075
2076 if (!inputToForgetWeightsPin.IsValid() ||
2077 !inputToCellWeightsPin.IsValid() ||
2078 !inputToOutputWeightsPin.IsValid() ||
2079 !recurrentToForgetWeightsPin.IsValid() ||
2080 !recurrentToCellWeightsPin.IsValid() ||
2081 !recurrentToOutputWeightsPin.IsValid() ||
2082 !forgetGateBiasPin.IsValid() ||
2083 !cellBiasPin.IsValid() ||
2084 !outputGateBiasPin.IsValid())
2085 {
2086 return Fail("%s: Operation has invalid tensor inputs", __func__);
2087 }
2088
2089 // Get the optional input tensors:
2090 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2091 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2092 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002093 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002094 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2095 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2096 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2097 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002098 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002099 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2100 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002101 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002102 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2103 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002104 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002105 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2106 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002107 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002108 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2109 const ConstTensorPin inputGateBiasPin =
2110 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2111 12,
2112 model,
2113 data,
2114 g_DontPermute,
2115 nullptr,
2116 true);
2117
2118 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2119 // [output_size, num_units].
2120 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002121 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002122 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2123 const ConstTensorPin projectionBiasPin =
2124 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2125 17,
2126 model,
2127 data,
2128 g_DontPermute,
2129 nullptr,
2130 true);
2131
2132 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2133 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2134 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2135 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2136 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2137 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2138 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2139 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2140 {
2141 return Fail("%s: Operation has invalid tensor inputs", __func__);
2142 }
2143
2144 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2145 // 20: The activation function: A value indicating the activation function:
2146 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2147 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2148 // If set to 0.0 then clipping is disabled.
2149 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2150 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2151 ActivationFn activation;
2152 float cellClip;
2153 float projClip;
2154 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2155 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2156 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2157 {
2158 return Fail("%s: Operation has invalid scalar inputs", __func__);
2159 }
2160
2161 // Get the normalization tensors
2162 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2163 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002164 const ConstTensorPin inputLayerNormWeightsPin
2165 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002166
2167 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2168 // Used to rescale normalized inputs to activation at forget gate.
2169 const ConstTensorPin forgetLayerNormWeightsPin =
2170 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2171 24,
2172 model,
2173 data,
2174 g_DontPermute,
2175 nullptr,
2176 true);
2177
2178 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2179 // Used to rescale normalized inputs to activation at cell gate.
2180 const ConstTensorPin cellLayerNormWeightsPin =
2181 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2182 25,
2183 model,
2184 data,
2185 g_DontPermute,
2186 nullptr,
2187 true);
2188
2189 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2190 // Used to rescale normalized inputs to activation at output gate.
2191 const ConstTensorPin outputLayerNormWeightsPin =
2192 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2193 26,
2194 model,
2195 data,
2196 g_DontPermute,
2197 nullptr,
2198 true);
2199
2200 // Outputs:
2201 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2202 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2203 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2204 if (!scratchBuffer)
2205 {
2206 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2207 }
2208 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2209 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2210 if (!outputStateOut)
2211 {
2212 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2213 }
2214 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2215 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2216 if (!cellStateOut)
2217 {
2218 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2219 }
2220 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2221 // effectively the same as the current “output state (out)” value.
2222 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2223 if (!output)
2224 {
2225 return Fail("%s: Could not read output 3: output", __func__);
2226 }
2227
2228 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002229 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002230 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2231 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2232 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2233 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2234 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2235 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2236 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2237 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2238 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2239 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2240 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2241 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2242 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2243 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2244 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2245 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2246 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2247 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2248 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2249 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2250 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2251
2252 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002253 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002254 desc.m_ActivationFunc = activation;
2255 desc.m_ClippingThresCell = cellClip;
2256 desc.m_ClippingThresProj = projClip;
2257 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2258 params.m_RecurrentToInputWeights == nullptr ||
2259 params.m_InputGateBias == nullptr);
2260 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2261 params.m_CellToOutputWeights != nullptr);
2262 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2263 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2264 params.m_ForgetLayerNormWeights != nullptr ||
2265 params.m_CellLayerNormWeights != nullptr ||
2266 params.m_OutputLayerNormWeights != nullptr);
2267
2268 // validate the optional input groups
2269 if (desc.m_CifgEnabled &&
2270 (params.m_InputToInputWeights != nullptr ||
2271 params.m_RecurrentToInputWeights != nullptr ||
2272 params.m_InputGateBias != nullptr))
2273 {
2274 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2275 " and input gate bias must be provided", __func__);
2276 }
2277
2278 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2279 {
2280 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2281 }
2282
2283 if (desc.m_PeepholeEnabled &&
2284 (params.m_CellToForgetWeights == nullptr ||
2285 params.m_CellToOutputWeights == nullptr ||
2286 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2287 {
2288 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2289 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2290 }
2291
2292 if (desc.m_LayerNormEnabled &&
2293 (params.m_ForgetLayerNormWeights == nullptr ||
2294 params.m_CellLayerNormWeights == nullptr ||
2295 params.m_OutputLayerNormWeights == nullptr ||
2296 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2297 {
2298 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2299 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2300 }
2301
2302 // Check if the layer is supported
2303 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002304 const TensorInfo& inputInfo = input.GetTensorInfo();
2305 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2306 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002307
2308 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002309 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2310 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2311 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2312 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002313
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002314 if (IsDynamicTensor(scratchBufferInfo) ||
2315 IsDynamicTensor(outputStateOutInfo) ||
2316 IsDynamicTensor(cellStateOutInfo) ||
2317 IsDynamicTensor(outputInfo))
2318 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002319 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2320 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2321 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002322 }
2323
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002324 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002325 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002326 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2327 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2328 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2329 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2330 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2331 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2332 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2333 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2334 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2335
2336 // Optional parameters
2337 if(!desc.m_CifgEnabled)
2338 {
2339 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2340 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2341 if (params.m_CellToInputWeights != nullptr)
2342 {
2343 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2344 }
2345 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2346 }
2347
2348 if(desc.m_ProjectionEnabled)
2349 {
2350 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2351 if (params.m_ProjectionBias != nullptr)
2352 {
2353 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2354 }
2355 }
2356
2357 if(desc.m_PeepholeEnabled)
2358 {
2359 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2360 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2361 }
2362
2363 if (desc.m_LayerNormEnabled)
2364 {
2365 if(!desc.m_CifgEnabled)
2366 {
2367 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2368 }
2369 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2370 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2371 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2372 }
2373
2374 bool isSupported = false;
2375 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2376 IsLstmSupported,
2377 data.m_Backends,
2378 isSupported,
2379 inputInfo,
2380 outputStateInInfo,
2381 cellStateInInfo,
2382 scratchBufferInfo,
2383 outputStateOutInfo,
2384 cellStateOutInfo,
2385 outputInfo,
2386 desc,
2387 paramsInfo);
2388 if (!isSupported)
2389 {
2390 return false;
2391 }
2392
2393 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002394 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002395
2396 input.Connect(layer->GetInputSlot(0));
2397 outputStateIn.Connect(layer->GetInputSlot(1));
2398 cellStateIn.Connect(layer->GetInputSlot(2));
2399
2400 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
2401 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2402 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2403 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2404}
2405
Sadik Armagan701d9a02019-09-04 15:16:18 +01002406bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2407{
2408 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002409 ActivationDescriptor desc;
2410 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002411
2412 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2413}
2414
Mike Kelly46272802019-08-14 17:00:48 +01002415bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2416{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002417 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002418 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2419}
2420
2421bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2422{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002423 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002424 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2425}
2426
2427bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2428{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002429 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002430 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2431}
2432
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002433bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002434{
2435 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2436
2437 if (!input.IsValid())
2438 {
2439 return Fail("%s: Operation has invalid inputs", __func__);
2440 }
2441
2442 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2443
2444 if (!output)
2445 {
2446 return Fail("%s: Could not read output 0", __func__);
2447 }
2448
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002449 const TensorInfo& inputInfo = input.GetTensorInfo();
2450 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002451 if (IsDynamicTensor(outputInfo))
2452 {
2453 return Fail("%s: Dynamic output tensors are not supported", __func__);
2454 }
2455
2456 // ArmNN does not currently support non-fixed weights or bias
2457 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2458 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2459
2460 if (weightsOperand == nullptr)
2461 {
2462 return Fail("%s: Operand is invalid", __func__);
2463 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002464 TransposeConvolution2dDescriptor desc;
2465 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002466
2467 // Determine whether padding is implicit or explicit
2468 bool implicitPadding = operation.inputs.size() == 9;
2469
2470 if (implicitPadding )
2471 {
2472 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2473 }
2474 else
2475 {
2476 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2477 }
2478
2479 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2480 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2481 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2482
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002483 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002484
2485 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2486 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002487 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002488 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2489 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2490
2491 // Bias is a 1D tensor
2492 const ConstTensorPin biasPin =
2493 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2494
2495 if (!weightsPin.IsValid())
2496 {
2497 return Fail("%s: Operation has invalid weights", __func__);
2498 }
2499
2500 if (!biasPin.IsValid())
2501 {
2502 return Fail("%s: Operation has invalid biases", __func__);
2503 }
2504
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002505 ConstTensor weights = weightsPin.GetConstTensor();
2506 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002507 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2508
2509 ActivationFn activation;
2510
2511 if (implicitPadding)
2512 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002513 int32_t strideX{0};
2514 int32_t strideY{0};
2515 int32_t padLeft{0};
2516 int32_t padRight{0};
2517 int32_t padTop{0};
2518 int32_t padBottom{0};
2519
David Monahan613b49c2019-06-27 11:37:47 +01002520 android::nn::PaddingScheme paddingScheme;
2521 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002522 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2523 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002524 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2525 {
2526 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2527 }
2528
2529 const uint32_t kernelX = weights.GetShape()[widthIndex];
2530 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002531 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2532 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002533
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002534 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2535 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2536
2537 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2538 // but Arm NN only supports values >= 0
2539 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2540 {
2541 return Fail("%s: Negative padding values are not supported", __func__);
2542 }
2543
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002544 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2545 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002546 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2547 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2548 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2549 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002550 }
2551 else if (operation.inputs.size() == 11)
2552 {
2553 // explicit padding
2554 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2555 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2556 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2557 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2558 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2559 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2560 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2561 {
2562 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2563 }
2564 }
2565 else
2566 {
2567 return Fail("%s: Unsupported number of operation inputs", __func__);
2568 }
2569
2570 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002571 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002572
2573 bool isSupported = false;
2574 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2575 IsTransposeConvolution2dSupported,
2576 data.m_Backends,
2577 isSupported,
2578 inputInfo,
2579 outputInfo,
2580 desc,
2581 weights.GetInfo(),
2582 biases);
2583 if (!isSupported)
2584 {
2585 return false;
2586 }
2587
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002588 IConnectableLayer* startLayer =
2589 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002590 if (!startLayer)
2591 {
2592 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2593 }
2594
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002595 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002596 if (!endLayer)
2597 {
2598 return Fail("%s: ProcessActivation failed", __func__);
2599 }
2600
2601 input.Connect(startLayer->GetInputSlot(0));
2602
2603 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2604}
2605
Mike Kellyb5fdf382019-06-11 16:35:25 +01002606} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002607} // namespace armnn_driver