blob: 5d6274fc15ffb148d6e2ac81f55230cb065b6e84 [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();
705 ConstTensor biases = biasesPin.GetConstTensor();
706 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
707
708 const TensorShape& inputShape = inputInfo.GetShape();
709 const TensorShape& outputShape = outputInfo.GetShape();
710 const TensorShape& weightsShape = weights.GetShape();
711 const TensorShape& biasesShape = biases.GetShape();
712
713 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
714 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
715 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
716 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
717
718 Convolution2dDescriptor desc;
719 desc.m_DataLayout = dataLayout;
720 desc.m_BiasEnabled = true;
721
722 int numGroups;
723 ActivationFn activation;
724
725 if (operation.inputs.size() == 12)
726 {
727 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
728 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
729 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
730 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
731 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
732 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
733 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
734 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
735 {
736 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
737 }
738
739 }
740 else if (operation.inputs.size() == 9)
741 {
742 android::nn::PaddingScheme paddingScheme;
743 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
744 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
745 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
746 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
747 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
748 {
749 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
750 }
751
752 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
753 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
754
755 const uint32_t kernelX = weightsShape[widthIndex];
756 const uint32_t kernelY = weightsShape[heightIndex];
757
758 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
759 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
760 }
761 else
762 {
763 return Fail("%s: Unsupported number of operation inputs", __func__);
764 }
765
766 const unsigned int outputChannels = outputShape[channelsIndex];
767
768 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
769 const unsigned int channelMultiplier = outputChannels / numGroups;
770
771 //
772 // Validate all relevant inputs
773 //
774 if (numGroups <= 0)
775 {
776 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
777 }
778
779 if (outputChannels % numGroups != 0u)
780 {
781 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
782 }
783
784 //
785 // Set up Splitter layer
786 //
787 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
788 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
789
790 TensorInfo splitterOutputInfo(4,
791 splitterDimSizes,
792 inputInfo.GetDataType(),
793 inputInfo.GetQuantizationScale(),
794 inputInfo.GetQuantizationOffset());
795
796 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
797
798 ViewsDescriptor splitterDesc(numGroups);
799 for (unsigned int group = 0u; group < numGroups; ++group)
800 {
801 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
802 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
803 {
804 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
805 }
806 }
807
808 bool isSupported = false;
809 FORWARD_LAYER_SUPPORT_FUNC(__func__,
810 IsSplitterSupported,
811 data.m_Backends,
812 isSupported,
813 inputInfo,
814 splitterOutputInfos,
815 splitterDesc);
816 if (!isSupported)
817 {
818 return false;
819 }
820
821 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
822 if (!splitterLayer)
823 {
824 return Fail("%s: Failed to add SplitterLayer", __func__);
825 }
826
827 input.Connect(splitterLayer->GetInputSlot(0));
828 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
829 {
830 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
831 }
832
833 //
834 // Set up Convolution2d layers for each group
835 //
836 TensorShape groupInputShape(inputShape);
837 groupInputShape[channelsIndex] = channelsPerGroup;
838
839 TensorShape groupOutputShape(outputShape);
840 groupOutputShape[channelsIndex] = 1;
841
842 TensorShape groupWeightsShape(weightsShape);
843 groupWeightsShape[0] /= channelMultiplier * numGroups;
844
845 TensorShape groupBiasesShape({ 1 });
846
847 const TensorInfo groupInputInfo (groupInputShape,
848 inputInfo.GetDataType(),
849 inputInfo.GetQuantizationScale(),
850 inputInfo.GetQuantizationOffset());
851 const TensorInfo groupWeightsInfo(groupWeightsShape,
852 weights.GetInfo().GetDataType(),
853 weights.GetInfo().GetQuantizationScale(),
854 weights.GetInfo().GetQuantizationOffset());
855 const TensorInfo groupBiasesInfo (groupBiasesShape,
856 biases.GetInfo().GetDataType(),
857 biases.GetInfo().GetQuantizationScale(),
858 biases.GetInfo().GetQuantizationOffset());
859 const TensorInfo groupOutputInfo (groupOutputShape,
860 outputInfo.GetDataType(),
861 outputInfo.GetQuantizationScale(),
862 outputInfo.GetQuantizationOffset());
863
864 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
865 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
866
867 std::vector<IConnectableLayer*> convLayers(numGroups*channelMultiplier, nullptr);
868 for (unsigned int group = 0u; group < numGroups; ++group)
869 {
870 for (unsigned int m = 0u; m < channelMultiplier; ++m)
871 {
872 auto index = group * channelMultiplier + m;
873
874 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
875 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
876
877 // Extract weights and biases data for current group convolution
878 ConstTensor groupWeights(groupWeightsInfo,
879 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
880 weightsDataOffset));
881 ConstTensor groupBiases(groupBiasesInfo,
882 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
883 biasesDataOffset));
884
885 isSupported = false;
886 FORWARD_LAYER_SUPPORT_FUNC(__func__,
887 IsConvolution2dSupported,
888 data.m_Backends,
889 isSupported,
890 groupInputInfo,
891 groupOutputInfo,
892 desc,
893 groupWeightsInfo,
894 Optional<TensorInfo>(groupBiasesInfo));
895 if (!isSupported)
896 {
897 return false;
898 }
899
900 IConnectableLayer *convLayer =
901 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
902 if (!convLayer)
903 {
904 return Fail("%s: AddConvolution2dLayer failed", __func__);
905 }
906
907 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
908 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
909
910 convLayers[index] = convLayer;
911 }
912 }
913
914 //
915 // Set up Concat layer
916 //
917 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
918 for (unsigned int group = 0u; group < numGroups; ++group)
919 {
920 for (unsigned int m = 0u; m < channelMultiplier; ++m)
921 {
922 auto index = group * channelMultiplier + m;
923 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
924 concatDescriptor.SetConcatAxis(channelsIndex);
925 }
926 }
927
928 isSupported = false;
929 FORWARD_LAYER_SUPPORT_FUNC(__func__,
930 IsConcatSupported,
931 data.m_Backends,
932 isSupported,
933 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
934 outputInfo,
935 concatDescriptor);
936 if (!isSupported)
937 {
938 return false;
939 }
940
941 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
942 if (!concatLayer)
943 {
944 return Fail("%s: AddConcatLayer failed", __func__);
945 }
946
947 for (unsigned int group = 0u; group < numGroups; ++group)
948 {
949 for (unsigned int m = 0u; m < channelMultiplier; ++m)
950 {
951 auto index = group * channelMultiplier + m;
952 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
953 }
954 }
955 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
956
957 //
958 // Set up Activation layer (if it is set)
959 //
960 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
961 if (!endLayer)
962 {
963 return Fail("%s: ProcessActivation failed", __func__);
964 }
965
966 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
967}
968
Aron Virginas-Tara2a73802019-10-09 15:30:40 +0100969bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
970{
971 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
972
973 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
974 if (!input.IsValid())
975 {
976 return Fail("%s: Operation has an invalid input 0", __func__);
977 }
978
979 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
980 if (!output)
981 {
982 return Fail("%s: Operation has an invalid output", __func__);
983 }
984
985 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
986 if (IsDynamicTensor(outputInfo))
987 {
988 return Fail("%s: Dynamic output tensors are not supported", __func__);
989 }
990
991 // Determine data type of input tensor
992 OperandType inputType;
993 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
994 {
995 return Fail("%s: Operation has invalid inputs", __func__);
996 }
997
998 InstanceNormalizationDescriptor desc;
999
1000 // Read gamma, beta & epsilon
1001 if (inputType == OperandType::TENSOR_FLOAT16)
1002 {
1003 Half fp16Gamma;
1004 Half fp16Beta;
1005 Half fp16Epsilon;
1006
1007 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1008 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1009 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1010 {
1011 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1012 }
1013
1014 desc.m_Gamma = static_cast<float>(fp16Gamma);
1015 desc.m_Beta = static_cast<float>(fp16Beta);
1016 desc.m_Eps = static_cast<float>(fp16Epsilon);
1017 }
1018 else if (inputType == OperandType::TENSOR_FLOAT32)
1019 {
1020 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1021 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1022 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1023 {
1024 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1025 }
1026 }
1027 else
1028 {
1029 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1030 }
1031
1032 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1033
1034 bool isSupported = false;
1035 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1036 IsInstanceNormalizationSupported,
1037 data.m_Backends,
1038 isSupported,
1039 input.GetTensorInfo(),
1040 outputInfo,
1041 desc);
1042 if (!isSupported)
1043 {
1044 return false;
1045 }
1046
1047 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1048 input.Connect(layer->GetInputSlot(0));
1049
1050 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1051}
1052
Mike Kelly46272802019-08-14 17:00:48 +01001053bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1054{
1055 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1056 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1057}
1058
Sadik Armagan15d63e22019-07-26 16:59:35 +01001059bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1060{
1061 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001062 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001063}
1064
Mike Kelly46272802019-08-14 17:00:48 +01001065bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1066 const Model& model,
1067 ConversionData& data)
1068{
1069 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1070 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1071}
1072
1073bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1074{
1075 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1076 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1077}
1078
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001079bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1080{
1081 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1082
1083 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1084 if (!input.IsValid())
1085 {
1086 return Fail("%s: Failed to read input 0", __func__);
1087 }
1088
1089 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1090 if (!output)
1091 {
1092 return Fail("%s: Failed to read output", __func__);
1093 }
1094
1095 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1096 if (IsDynamicTensor(outputInfo))
1097 {
1098 return Fail("%s: Dynamic output tensors are not supported", __func__);
1099 }
1100
1101 // Determine data type of input tensor
1102 OperandType inputType;
1103 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1104 {
1105 return Fail("%s: Operation has invalid inputs", __func__);
1106 }
1107
1108 LogSoftmaxDescriptor descriptor;
1109
1110 // Read beta
1111 if (inputType == OperandType::TENSOR_FLOAT16)
1112 {
1113 Half fp16Beta;
1114 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1115 {
1116 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1117 }
1118
1119 descriptor.m_Beta = static_cast<float>(fp16Beta);
1120 }
1121 else if (inputType == OperandType::TENSOR_FLOAT32)
1122 {
1123 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1124 {
1125 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1126 }
1127 }
1128 else
1129 {
1130 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1131 }
1132
1133 // Read axis
1134 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1135 {
1136 return Fail("%s: Failed to read input 2", __func__);
1137 }
1138
1139 bool isSupported = false;
1140 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1141 IsLogSoftmaxSupported,
1142 data.m_Backends,
1143 isSupported,
1144 input.GetTensorInfo(),
1145 outputInfo,
1146 descriptor);
1147 if (!isSupported)
1148 {
1149 return false;
1150 }
1151
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001152 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001153 if (!layer)
1154 {
1155 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1156 }
1157
1158 input.Connect(layer->GetInputSlot(0));
1159
1160 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1161}
1162
Sadik Armagan15d63e22019-07-26 16:59:35 +01001163bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1164{
1165 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001166 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001167}
1168
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001169bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1170{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001171 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1172
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001173 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1174 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1175
1176 if (!input0.IsValid() || !input1.IsValid())
1177 {
1178 return Fail("%s: Operation has invalid inputs", __func__);
1179 }
1180
1181 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1182 if (!outputOperand)
1183 {
1184 return Fail("%s: Could not read output", __func__);
1185 }
1186
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001187 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001188 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001189 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001190 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001191 }
1192
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001193 bool isSupported = false;
1194 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1195 IsMaximumSupported,
1196 data.m_Backends,
1197 isSupported,
1198 input0.GetTensorInfo(),
1199 input1.GetTensorInfo(),
1200 outInfo);
1201
1202 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001203 {
1204 return false;
1205 }
1206
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001207 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001208 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001209 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1210 if (!isReshapeSupported)
1211 {
1212 return false;
1213 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001214
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001215 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001216}
1217
Mike Kelly46272802019-08-14 17:00:48 +01001218bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1219{
1220 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1221 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1222}
1223
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001224bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1225{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001226 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1227
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001228 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1229 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1230
1231 if (!input0.IsValid() || !input1.IsValid())
1232 {
1233 return Fail("%s: Operation has invalid inputs", __func__);
1234 }
1235
1236 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1237 if (!output)
1238 {
1239 return Fail("%s: Could not read output 0", __func__);
1240 }
1241
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001242 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001243 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001244 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001245 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001246 }
1247
1248 bool isSupported = false;
1249 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1250 IsMinimumSupported,
1251 data.m_Backends,
1252 isSupported,
1253 input0.GetTensorInfo(),
1254 input1.GetTensorInfo(),
1255 outputInfo);
1256
1257 if (!isSupported)
1258 {
1259 return false;
1260 }
1261
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001262 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001263 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001264 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1265 if (!isReshapeSupported)
1266 {
1267 return false;
1268 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001269
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001270 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001271}
1272
Mike Kelly46272802019-08-14 17:00:48 +01001273bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1274{
1275 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1276 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1277}
1278
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001279bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1280{
1281 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1282 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1283}
1284
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001285bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1286{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001287 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1288
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001289 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1290 if (!input.IsValid())
1291 {
1292 return Fail("%s: Could not read input 0", __func__);
1293 }
1294
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001295 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1296 if (!output)
1297 {
1298 return Fail("%s: Could not read output", __func__);
1299 }
1300
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001301 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001302 unsigned int rank = inputInfo.GetNumDimensions();
1303
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001304 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001305 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1306 {
1307 return Fail("%s: Could not convert paddings", __func__);
1308 }
1309
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001310 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001311 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001312 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001313 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001314 }
1315
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001316 // Determine type of padding value
1317 OperandType operandType0;
1318 OperandType operandType2;
1319
1320 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1321 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1322 {
1323 return Fail("%s: Operation has invalid inputs", __func__);
1324 }
1325
1326 // Read value to use for padding
1327 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1328 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001329 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001330 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1331 {
1332 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1333 }
1334
1335 descriptor.m_PadValue = f16PadValue;
1336 }
1337 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1338 {
1339 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1340 {
1341 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1342 }
1343 }
1344 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1345 {
Mike Kelly3c673942019-07-25 09:26:06 +01001346 int32_t intPadValue = 0;
1347 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001348 {
1349 return Fail("%s: Could not read input 2 (INT32)", __func__);
1350 }
Mike Kelly3c673942019-07-25 09:26:06 +01001351 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001352 }
1353 else
1354 {
1355 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1356 }
1357
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001358 bool isSupported = false;
1359 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1360 IsPadSupported,
1361 data.m_Backends,
1362 isSupported,
1363 inputInfo,
1364 outputInfo,
1365 descriptor);
1366 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001367 {
1368 return false;
1369 }
1370
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001371 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001372 assert(layer != nullptr);
1373 input.Connect(layer->GetInputSlot(0));
1374 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1375
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001376 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001377}
1378
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001379bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1380{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001381 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1382
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001383 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1384 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1385
1386 if (!input.IsValid() || !alpha.IsValid())
1387 {
1388 return Fail("%s: Operation has invalid inputs", __func__);
1389 }
1390
1391 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1392
1393 if (!output)
1394 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001395 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001396 }
1397
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001398 const TensorInfo& inputInfo = input.GetTensorInfo();
1399 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1400 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001401
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001402 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001403 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001404 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001405 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001406
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001407 bool isSupported = false;
1408 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1409 IsPreluSupported,
1410 data.m_Backends,
1411 isSupported,
1412 inputInfo,
1413 alphaInfo,
1414 outputInfo);
1415 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001416 {
1417 return false;
1418 }
1419
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001420 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001421
1422 if (!layer)
1423 {
1424 return Fail("%s: AddPreluLayer failed", __func__);
1425 }
1426
Sadik Armagan64b19b52019-08-19 09:49:58 +01001427 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1428 if (!isReshapeSupported)
1429 {
1430 return false;
1431 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001432
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001433 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001434}
1435
Sadik Armagan5a476a82019-07-30 09:43:18 +01001436bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1437{
1438 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1439
1440 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1441 if (!input.IsValid())
1442 {
1443 return Fail("%s: Operation has invalid input", __func__);
1444 }
1445
1446 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1447 if (!outputOperand)
1448 {
1449 return Fail("%s: Operation has invalid outputs", __func__);
1450 }
1451
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001452 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001453 if (IsDynamicTensor(outputInfo))
1454 {
1455 return Fail("%s: Dynamic output tensors are not supported", __func__);
1456 }
1457
1458 bool isSupported = false;
1459 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1460 IsQuantizeSupported,
1461 data.m_Backends,
1462 isSupported,
1463 input.GetTensorInfo(),
1464 outputInfo);
1465 if (!isSupported)
1466 {
1467 return false;
1468 }
1469
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001470 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001471 assert(layer != nullptr);
1472 input.Connect(layer->GetInputSlot(0));
1473
1474 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1475}
1476
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001477bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1478{
1479 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1480
1481 //Inputs:
1482 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1483 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1484 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1485 if (!input.IsValid())
1486 {
1487 return Fail("%s: Could not read input 0: input", __func__);
1488 }
1489
1490 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1491 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1492 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1493 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1494 if (!previousCellStateIn.IsValid())
1495 {
1496 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1497 }
1498
1499 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1500 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1501 // is quantized with a fixed quantization range of -1, 127/128.
1502 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1503 if (!previousOutputIn.IsValid())
1504 {
1505 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1506 }
1507
1508 // Get the input tensors:
1509 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1510 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1511 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1512 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001513 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001514
1515 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1516 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1517 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1518 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001519 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001520
1521 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1522 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1523 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1524 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001525 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001526
1527 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1528 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1529 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1530 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001531 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001532
1533 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1534 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1535 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1536 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001537 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001538
1539 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1540 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1541 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1542 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001543 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001544
1545 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1546 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1547 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1548 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001549 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001550
1551 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1552 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1553 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1554 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001555 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001556
1557 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1558 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1559 // of input and weights scales and zeroPoint equal to 0.
1560 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001561 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001562
1563 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1564 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1565 // of input and weights scales and zeroPoint equal to 0.
1566 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001567 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001568
1569 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1570 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1571 // and weights scales and zeroPoint equal to 0.
1572 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001573 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001574
1575 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1576 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1577 // of input and weights scales and zeroPoint equal to 0.
1578 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001579 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001580
1581 if (!inputToInputWeightsPin.IsValid() ||
1582 !inputToForgetWeightsPin.IsValid() ||
1583 !inputToCellWeightsPin.IsValid() ||
1584 !inputToOutputWeightsPin.IsValid() ||
1585 !recurrentToInputWeightsPin.IsValid() ||
1586 !recurrentToForgetWeightsPin.IsValid() ||
1587 !recurrentToCellWeightsPin.IsValid() ||
1588 !recurrentToOutputWeightsPin.IsValid() ||
1589 !inputGateBiasPin.IsValid() ||
1590 !forgetGateBiasPin.IsValid() ||
1591 !cellBiasPin.IsValid() ||
1592 !outputGateBiasPin.IsValid())
1593 {
1594 return Fail("%s: Operation has invalid tensor inputs", __func__);
1595 }
1596
1597 // Outputs:
1598 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1599 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1600 // of -2^4, 2^4 * 32767/32768.
1601 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1602 if (!cellStateOut)
1603 {
1604 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1605 }
1606
1607 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1608 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1609 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1610 if (!output)
1611 {
1612 return Fail("%s: Could not read output 1: output", __func__);
1613 }
1614
1615 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001616 const TensorInfo& inputInfo = input.GetTensorInfo();
1617 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1618 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001619
1620 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001621 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1622 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001623
1624 // Dynamic tensors currently not supported
1625 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1626 {
1627 return Fail("%s: Dynamic output tensors are not supported", __func__);
1628 }
1629
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001630 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001631
1632 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1633 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1634 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1635 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1636 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1637 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1638 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1639 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1640 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1641 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1642 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1643 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1644
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001645 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001646 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1647 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1648 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1649 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1650 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1651 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1652 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1653 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1654 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1655 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1656 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1657 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1658
1659 bool isSupported = false;
1660 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1661 IsQuantizedLstmSupported,
1662 data.m_Backends,
1663 isSupported,
1664 inputInfo,
1665 previousCellStateInInfo,
1666 previousOutputInInfo,
1667 cellStateOutInfo,
1668 outputInfo,
1669 paramsInfo);
1670
1671 if (!isSupported)
1672 {
1673 return false;
1674 }
1675
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001676 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001677 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001678 previousCellStateIn.Connect(layer->GetInputSlot(1));
1679 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001680
1681 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1682 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1683}
1684
Sadik Armagan61113162019-07-25 09:09:40 +01001685bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1686{
1687 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1688 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1689}
1690
1691bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1692{
1693 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1694 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1695}
1696
1697bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1698{
1699 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1700 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1701}
1702
Mike Kelly46272802019-08-14 17:00:48 +01001703bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1704{
1705 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1706 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1707}
1708
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001709bool HalPolicy::ConvertResize(const Operation& operation,
1710 const Model& model,
1711 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001712 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001713{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001714 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001715 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001716
1717 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001718 if (!input.IsValid())
1719 {
1720 return Fail("%s: Could not read input 0", __func__);
1721 }
1722
1723 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1724 if (!output)
1725 {
1726 return Fail("%s: Could not read output 0", __func__);
1727 }
1728
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001729 const TensorInfo& inputInfo = input.GetTensorInfo();
1730 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001731
1732 if (IsDynamicTensor(outputInfo))
1733 {
1734 return Fail("%s: Dynamic output tensors are not supported", __func__);
1735 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001736
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001737 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001738 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001739 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1740
1741 OperandType operandType1;
1742 OperandType operandType2;
1743
1744 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1745 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1746 {
1747 return Fail("%s: Operation has invalid inputs", __func__);
1748 }
1749
1750 if (operandType1 != operandType2)
1751 {
1752 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1753 }
1754
1755 if (operandType1 == OperandType::INT32)
1756 {
1757 // Case 1: resizing by shape
1758 int32_t targetWidth = 0;
1759 int32_t targetHeight = 0;
1760
1761 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1762 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1763 {
1764 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1765 }
1766
1767 if (targetWidth < 0 || targetHeight < 0)
1768 {
1769 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1770 "Target width/height cannot be < 0", __func__);
1771 }
1772
1773 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001774 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001775 }
1776 else if (operandType1 == OperandType::FLOAT32)
1777 {
1778 // Case 2: resizing by scale
1779 float widthScale = 1.0f;
1780 float heightScale = 1.0f;
1781
1782 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1783 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1784 {
1785 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1786 }
1787
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001788 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001789 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1790
1791 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1792 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1793
1794 descriptor.m_TargetWidth = std::floor(width * widthScale);
1795 descriptor.m_TargetHeight = std::floor(height * heightScale);
1796 }
1797 else
1798 {
1799 // NOTE: FLOAT16 scales are not supported
1800 return false;
1801 }
1802
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001803 bool isSupported = false;
1804 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1805 IsResizeSupported,
1806 data.m_Backends,
1807 isSupported,
1808 inputInfo,
1809 outputInfo,
1810 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001811
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001812 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001813 {
1814 return false;
1815 }
1816
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001817 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001818
1819 assert(layer != nullptr);
1820
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001821 input.Connect(layer->GetInputSlot(0));
1822
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001823 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001824}
1825
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001826bool HalPolicy::ConvertRsqrt(const Operation& operation, const Model& model, ConversionData& data)
1827{
1828 ALOGV("hal_1_2::HalPolicy::ConvertRsqrt()");
1829
1830 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1831 if (!input.IsValid())
1832 {
1833 return Fail("%s: Operation has invalid input", __func__);
1834 }
1835
1836 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1837 if (!output)
1838 {
1839 return Fail("%s: Could not read output 0", __func__);
1840 }
1841
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001842 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001843 if (IsDynamicTensor(outputInfo))
1844 {
1845 return Fail("%s: Dynamic output tensors are not supported", __func__);
1846 }
1847
1848 bool isSupported = false;
1849 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1850 IsRsqrtSupported,
1851 data.m_Backends,
1852 isSupported,
1853 input.GetTensorInfo(),
1854 outputInfo);
1855
1856 if (!isSupported)
1857 {
1858 return false;
1859 }
1860
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001861 IConnectableLayer* const layer = data.m_Network->AddRsqrtLayer();
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001862 assert(layer != nullptr);
1863 input.Connect(layer->GetInputSlot(0));
1864
1865 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1866}
1867
Finn Williamsd74c5052019-07-30 17:06:00 +01001868bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1869{
1870 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1871 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1872}
1873
Keith Davisa6bc52f2019-06-26 09:39:49 +01001874bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1875{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001876 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001877
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001878 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001879 if (!input.IsValid() )
1880 {
1881 return Fail("%s: Operation has invalid inputs", __func__);
1882 }
1883
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001884 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001885 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001886 if (rank != 4)
1887 {
1888 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1889 }
1890
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001891 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1892 if (!output)
1893 {
1894 return Fail("%s: Could not read output 0", __func__);
1895 }
1896
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001897 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001898 if (IsDynamicTensor(outputInfo))
1899 {
1900 return Fail("%s: Dynamic output tensors are not supported", __func__);
1901 }
1902
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001903 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01001904
1905 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1906
1907 if (desc.m_BlockSize <= 1)
1908 {
1909 return Fail("%s: Block size must be at least 1 in all dimensions");
1910 }
1911
1912 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1913
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001914 bool isSupported = false;
1915 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1916 IsSpaceToDepthSupported,
1917 data.m_Backends,
1918 isSupported,
1919 inputInfo,
1920 outputInfo,
1921 desc);
1922 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01001923 {
1924 return false;
1925 }
1926
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001927 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001928 assert(layer != nullptr);
1929 input.Connect(layer->GetInputSlot(0));
1930
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001931 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001932}
1933
Francis Murtagh074c25a2019-07-22 16:40:57 +01001934bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1935{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001936 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1937
Francis Murtagh074c25a2019-07-22 16:40:57 +01001938 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1939 if (!input.IsValid())
1940 {
1941 return Fail("%s: Operation has invalid inputs", __func__);
1942 }
1943
1944 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1945 if (!outputOperand)
1946 {
1947 return Fail("%s: Operation has no outputs", __func__);
1948 }
1949
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001950 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001951 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001952 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001953 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001954 }
1955
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001956 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01001957 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1958 {
1959 return Fail("%s: Operation has invalid inputs", __func__);
1960 }
1961
1962 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1963 2,
1964 HalPolicy::OperandType::INT32,
1965 desc.m_Axis,
1966 model,
1967 data))
1968 {
1969 return Fail("%s: Operation has invalid inputs", __func__);
1970 }
1971
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01001972 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
1973 !(desc.m_Axis == 1 ||
1974 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
1975 {
1976 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
1977 }
1978
Francis Murtagh074c25a2019-07-22 16:40:57 +01001979 bool isSupported = false;
1980 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1981 IsSoftmaxSupported,
1982 data.m_Backends,
1983 isSupported,
1984 input.GetTensorInfo(),
1985 outputInfo,
1986 desc);
1987 if (!isSupported)
1988 {
1989 return false;
1990 }
1991
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001992 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001993 assert(layer != nullptr);
1994 input.Connect(layer->GetInputSlot(0));
1995
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001996 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001997}
1998
Mike Kelly0a879362019-07-29 16:56:31 +01001999bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2000{
2001 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2002 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2003}
2004
Sadik Armagan61113162019-07-25 09:09:40 +01002005bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2006{
2007 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2008 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2009}
2010
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002011bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2012{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002013 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2014
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002015 // Inputs:
2016 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2017 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2018 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2019 if (!input.IsValid())
2020 {
2021 return Fail("%s: Could not read input 0: input", __func__);
2022 }
2023 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2024 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2025 if (!outputStateIn.IsValid())
2026 {
2027 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2028 }
2029 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2030 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2031 if (!cellStateIn.IsValid())
2032 {
2033 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2034 }
2035
2036 // Get the mandatory input tensors:
2037 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2038 // [num_units, input_size].
2039 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002040 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002041 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2042 // [num_units, input_size].
2043 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002044 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002045 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2046 // [num_units, input_size].
2047 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002048 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002049 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2050 // [num_units, output_size].
2051 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002052 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002053 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2054 // [num_units, output_size].
2055 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002056 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002057 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2058 // [num_units, output_size].
2059 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002060 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002061 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2062 const ConstTensorPin forgetGateBiasPin =
2063 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2064 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2065 const ConstTensorPin cellBiasPin =
2066 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2067 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2068 const ConstTensorPin outputGateBiasPin =
2069 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2070
2071 if (!inputToForgetWeightsPin.IsValid() ||
2072 !inputToCellWeightsPin.IsValid() ||
2073 !inputToOutputWeightsPin.IsValid() ||
2074 !recurrentToForgetWeightsPin.IsValid() ||
2075 !recurrentToCellWeightsPin.IsValid() ||
2076 !recurrentToOutputWeightsPin.IsValid() ||
2077 !forgetGateBiasPin.IsValid() ||
2078 !cellBiasPin.IsValid() ||
2079 !outputGateBiasPin.IsValid())
2080 {
2081 return Fail("%s: Operation has invalid tensor inputs", __func__);
2082 }
2083
2084 // Get the optional input tensors:
2085 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2086 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2087 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002088 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002089 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2090 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2091 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2092 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002093 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002094 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2095 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002096 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002097 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2098 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002099 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002100 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2101 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002102 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002103 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2104 const ConstTensorPin inputGateBiasPin =
2105 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2106 12,
2107 model,
2108 data,
2109 g_DontPermute,
2110 nullptr,
2111 true);
2112
2113 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2114 // [output_size, num_units].
2115 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002116 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002117 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2118 const ConstTensorPin projectionBiasPin =
2119 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2120 17,
2121 model,
2122 data,
2123 g_DontPermute,
2124 nullptr,
2125 true);
2126
2127 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2128 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2129 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2130 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2131 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2132 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2133 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2134 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2135 {
2136 return Fail("%s: Operation has invalid tensor inputs", __func__);
2137 }
2138
2139 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2140 // 20: The activation function: A value indicating the activation function:
2141 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2142 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2143 // If set to 0.0 then clipping is disabled.
2144 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2145 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2146 ActivationFn activation;
2147 float cellClip;
2148 float projClip;
2149 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2150 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2151 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2152 {
2153 return Fail("%s: Operation has invalid scalar inputs", __func__);
2154 }
2155
2156 // Get the normalization tensors
2157 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2158 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002159 const ConstTensorPin inputLayerNormWeightsPin
2160 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002161
2162 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2163 // Used to rescale normalized inputs to activation at forget gate.
2164 const ConstTensorPin forgetLayerNormWeightsPin =
2165 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2166 24,
2167 model,
2168 data,
2169 g_DontPermute,
2170 nullptr,
2171 true);
2172
2173 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2174 // Used to rescale normalized inputs to activation at cell gate.
2175 const ConstTensorPin cellLayerNormWeightsPin =
2176 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2177 25,
2178 model,
2179 data,
2180 g_DontPermute,
2181 nullptr,
2182 true);
2183
2184 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2185 // Used to rescale normalized inputs to activation at output gate.
2186 const ConstTensorPin outputLayerNormWeightsPin =
2187 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2188 26,
2189 model,
2190 data,
2191 g_DontPermute,
2192 nullptr,
2193 true);
2194
2195 // Outputs:
2196 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2197 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2198 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2199 if (!scratchBuffer)
2200 {
2201 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2202 }
2203 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2204 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2205 if (!outputStateOut)
2206 {
2207 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2208 }
2209 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2210 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2211 if (!cellStateOut)
2212 {
2213 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2214 }
2215 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2216 // effectively the same as the current “output state (out)” value.
2217 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2218 if (!output)
2219 {
2220 return Fail("%s: Could not read output 3: output", __func__);
2221 }
2222
2223 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002224 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002225 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2226 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2227 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2228 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2229 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2230 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2231 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2232 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2233 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2234 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2235 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2236 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2237 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2238 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2239 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2240 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2241 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2242 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2243 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2244 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2245 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2246
2247 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002248 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002249 desc.m_ActivationFunc = activation;
2250 desc.m_ClippingThresCell = cellClip;
2251 desc.m_ClippingThresProj = projClip;
2252 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2253 params.m_RecurrentToInputWeights == nullptr ||
2254 params.m_InputGateBias == nullptr);
2255 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2256 params.m_CellToOutputWeights != nullptr);
2257 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2258 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2259 params.m_ForgetLayerNormWeights != nullptr ||
2260 params.m_CellLayerNormWeights != nullptr ||
2261 params.m_OutputLayerNormWeights != nullptr);
2262
2263 // validate the optional input groups
2264 if (desc.m_CifgEnabled &&
2265 (params.m_InputToInputWeights != nullptr ||
2266 params.m_RecurrentToInputWeights != nullptr ||
2267 params.m_InputGateBias != nullptr))
2268 {
2269 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2270 " and input gate bias must be provided", __func__);
2271 }
2272
2273 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2274 {
2275 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2276 }
2277
2278 if (desc.m_PeepholeEnabled &&
2279 (params.m_CellToForgetWeights == nullptr ||
2280 params.m_CellToOutputWeights == nullptr ||
2281 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2282 {
2283 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2284 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2285 }
2286
2287 if (desc.m_LayerNormEnabled &&
2288 (params.m_ForgetLayerNormWeights == nullptr ||
2289 params.m_CellLayerNormWeights == nullptr ||
2290 params.m_OutputLayerNormWeights == nullptr ||
2291 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2292 {
2293 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2294 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2295 }
2296
2297 // Check if the layer is supported
2298 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002299 const TensorInfo& inputInfo = input.GetTensorInfo();
2300 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2301 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002302
2303 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002304 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2305 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2306 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2307 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002308
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002309 if (IsDynamicTensor(scratchBufferInfo) ||
2310 IsDynamicTensor(outputStateOutInfo) ||
2311 IsDynamicTensor(cellStateOutInfo) ||
2312 IsDynamicTensor(outputInfo))
2313 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002314 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2315 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2316 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002317 }
2318
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002319 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002320 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002321 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2322 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2323 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2324 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2325 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2326 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2327 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2328 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2329 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2330
2331 // Optional parameters
2332 if(!desc.m_CifgEnabled)
2333 {
2334 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2335 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2336 if (params.m_CellToInputWeights != nullptr)
2337 {
2338 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2339 }
2340 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2341 }
2342
2343 if(desc.m_ProjectionEnabled)
2344 {
2345 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2346 if (params.m_ProjectionBias != nullptr)
2347 {
2348 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2349 }
2350 }
2351
2352 if(desc.m_PeepholeEnabled)
2353 {
2354 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2355 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2356 }
2357
2358 if (desc.m_LayerNormEnabled)
2359 {
2360 if(!desc.m_CifgEnabled)
2361 {
2362 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2363 }
2364 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2365 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2366 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2367 }
2368
2369 bool isSupported = false;
2370 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2371 IsLstmSupported,
2372 data.m_Backends,
2373 isSupported,
2374 inputInfo,
2375 outputStateInInfo,
2376 cellStateInInfo,
2377 scratchBufferInfo,
2378 outputStateOutInfo,
2379 cellStateOutInfo,
2380 outputInfo,
2381 desc,
2382 paramsInfo);
2383 if (!isSupported)
2384 {
2385 return false;
2386 }
2387
2388 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002389 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002390
2391 input.Connect(layer->GetInputSlot(0));
2392 outputStateIn.Connect(layer->GetInputSlot(1));
2393 cellStateIn.Connect(layer->GetInputSlot(2));
2394
2395 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
2396 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2397 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2398 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2399}
2400
Sadik Armagan701d9a02019-09-04 15:16:18 +01002401bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2402{
2403 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002404 ActivationDescriptor desc;
2405 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002406
2407 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2408}
2409
Mike Kelly46272802019-08-14 17:00:48 +01002410bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2411{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002412 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002413 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2414}
2415
2416bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2417{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002418 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002419 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2420}
2421
2422bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2423{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002424 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002425 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2426}
2427
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002428bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002429{
2430 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2431
2432 if (!input.IsValid())
2433 {
2434 return Fail("%s: Operation has invalid inputs", __func__);
2435 }
2436
2437 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2438
2439 if (!output)
2440 {
2441 return Fail("%s: Could not read output 0", __func__);
2442 }
2443
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002444 const TensorInfo& inputInfo = input.GetTensorInfo();
2445 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002446 if (IsDynamicTensor(outputInfo))
2447 {
2448 return Fail("%s: Dynamic output tensors are not supported", __func__);
2449 }
2450
2451 // ArmNN does not currently support non-fixed weights or bias
2452 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2453 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2454
2455 if (weightsOperand == nullptr)
2456 {
2457 return Fail("%s: Operand is invalid", __func__);
2458 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002459 TransposeConvolution2dDescriptor desc;
2460 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002461
2462 // Determine whether padding is implicit or explicit
2463 bool implicitPadding = operation.inputs.size() == 9;
2464
2465 if (implicitPadding )
2466 {
2467 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2468 }
2469 else
2470 {
2471 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2472 }
2473
2474 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2475 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2476 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2477
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002478 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002479
2480 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2481 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002482 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002483 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2484 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2485
2486 // Bias is a 1D tensor
2487 const ConstTensorPin biasPin =
2488 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2489
2490 if (!weightsPin.IsValid())
2491 {
2492 return Fail("%s: Operation has invalid weights", __func__);
2493 }
2494
2495 if (!biasPin.IsValid())
2496 {
2497 return Fail("%s: Operation has invalid biases", __func__);
2498 }
2499
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002500 ConstTensor weights = weightsPin.GetConstTensor();
2501 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002502 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2503
2504 ActivationFn activation;
2505
2506 if (implicitPadding)
2507 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002508 int32_t strideX{0};
2509 int32_t strideY{0};
2510 int32_t padLeft{0};
2511 int32_t padRight{0};
2512 int32_t padTop{0};
2513 int32_t padBottom{0};
2514
David Monahan613b49c2019-06-27 11:37:47 +01002515 android::nn::PaddingScheme paddingScheme;
2516 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002517 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2518 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002519 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2520 {
2521 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2522 }
2523
2524 const uint32_t kernelX = weights.GetShape()[widthIndex];
2525 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002526 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2527 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002528
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002529 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2530 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2531
2532 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2533 // but Arm NN only supports values >= 0
2534 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2535 {
2536 return Fail("%s: Negative padding values are not supported", __func__);
2537 }
2538
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002539 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2540 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002541 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2542 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2543 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2544 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002545 }
2546 else if (operation.inputs.size() == 11)
2547 {
2548 // explicit padding
2549 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2550 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2551 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2552 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2553 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2554 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2555 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2556 {
2557 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2558 }
2559 }
2560 else
2561 {
2562 return Fail("%s: Unsupported number of operation inputs", __func__);
2563 }
2564
2565 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002566 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002567
2568 bool isSupported = false;
2569 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2570 IsTransposeConvolution2dSupported,
2571 data.m_Backends,
2572 isSupported,
2573 inputInfo,
2574 outputInfo,
2575 desc,
2576 weights.GetInfo(),
2577 biases);
2578 if (!isSupported)
2579 {
2580 return false;
2581 }
2582
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002583 IConnectableLayer* startLayer =
2584 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002585 if (!startLayer)
2586 {
2587 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2588 }
2589
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002590 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002591 if (!endLayer)
2592 {
2593 return Fail("%s: ProcessActivation failed", __func__);
2594 }
2595
2596 input.Connect(startLayer->GetInputSlot(0));
2597
2598 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2599}
2600
Mike Kellyb5fdf382019-06-11 16:35:25 +01002601} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002602} // namespace armnn_driver