blob: c8e296882d0c3edecab5871dc14b684014e3dc37 [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
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +000026namespace
27{
28
29bool IsQSymmDequantizeForWeights(const Operation& operation, const Model& model)
30{
31 const Operand* operand = GetInputOperand<hal_1_2::HalPolicy>(operation, 0, model);
32 if (!operand)
33 {
34 return false;
35 }
36
37 if(!IsQSymm8(*operand))
38 {
39 // Only QSymm8 weights are dequantized on the fly by the driver
40 return false;
41 }
42
43 if (!IsOperandConstant<hal_1_2::HalPolicy>(*operand))
44 {
45 // Non-const input is not accepted for weights
46 return false;
47 }
48
49 // Iterate through all the operations and find the operation feeding from the Dequantize output
50 const size_t outputIndex = operation.outputs[0];
51 for (uint32_t operationIdx = 0; operationIdx < model.operations.size(); ++operationIdx)
52 {
53 const auto& operationIt = model.operations[operationIdx];
54 switch (operationIt.type)
55 {
56 case HalPolicy::OperationType::FULLY_CONNECTED:
57 if (outputIndex == operationIt.inputs[1]) // Weights are bound to slot 1
58 {
59 // If the output is going into the FC weights return true
60 return true;
61 }
62 break;
63 case HalPolicy::OperationType::LSTM:
64 for (size_t k = 0; k < operationIt.inputs.size(); ++k)
65 {
66 if (outputIndex == operationIt.inputs[k])
67 {
68 // If the output is going into the LSTM weights return true
69 return true;
70 }
71 }
72 break;
73 default:
74 break;
75 }
76 }
77
78 return false;
79}
80
81} // anonymous namespace
82
Mike Kellyb5fdf382019-06-11 16:35:25 +010083bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
84{
Mike Kellyb5fdf382019-06-11 16:35:25 +010085 switch (operation.type)
86 {
Kevin May407718f2019-09-09 14:46:41 +010087 case V1_2::OperationType::ABS:
88 return ConvertAbs(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +010089 case V1_2::OperationType::ADD:
90 return ConvertAdd(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +010091 case V1_2::OperationType::AVERAGE_POOL_2D:
92 return ConvertAveragePool2d(operation, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +010093 case V1_2::OperationType::BATCH_TO_SPACE_ND:
94 return ConvertBatchToSpaceNd(operation, model, data);
Mike Kellyb8805202019-07-31 17:25:43 +010095 case V1_2::OperationType::CONCATENATION:
96 return ConvertConcatenation(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +010097 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +010098 return ConvertConv2d(operation, model, data);
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +010099 case V1_2::OperationType::DEPTH_TO_SPACE:
100 return ConvertDepthToSpace(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100101 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100102 return ConvertDepthwiseConv2d(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100103 case V1_2::OperationType::DEQUANTIZE:
104 return ConvertDequantize(operation, model, data);
105 case V1_2::OperationType::DIV:
106 return ConvertDiv(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000107 case V1_2::OperationType::EQUAL:
108 return ConvertComparison(operation, model, data, ComparisonOperation::Equal);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100109 case V1_2::OperationType::EXPAND_DIMS:
110 return ConvertExpandDims(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100111 case V1_2::OperationType::FLOOR:
112 return ConvertFloor(operation, model, data);
113 case V1_2::OperationType::FULLY_CONNECTED:
114 return ConvertFullyConnected(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000115 case V1_2::OperationType::GREATER:
116 return ConvertComparison(operation, model, data, ComparisonOperation::Greater);
117 case V1_2::OperationType::GREATER_EQUAL:
118 return ConvertComparison(operation, model, data, ComparisonOperation::GreaterOrEqual);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100119 case V1_2::OperationType::GROUPED_CONV_2D:
120 return ConvertGroupedConv2d(operation, model, data);
Aron Virginas-Tara2a73802019-10-09 15:30:40 +0100121 case V1_2::OperationType::INSTANCE_NORMALIZATION:
122 return ConvertInstanceNormalization(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100123 case V1_2::OperationType::L2_NORMALIZATION:
124 return ConvertL2Normalization(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100125 case V1_2::OperationType::L2_POOL_2D:
126 return ConvertL2Pool2d(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000127 case V1_2::OperationType::LESS:
128 return ConvertComparison(operation, model, data, ComparisonOperation::Less);
129 case V1_2::OperationType::LESS_EQUAL:
130 return ConvertComparison(operation, model, data, ComparisonOperation::LessOrEqual);
Mike Kelly46272802019-08-14 17:00:48 +0100131 case V1_2::OperationType::LOCAL_RESPONSE_NORMALIZATION:
132 return ConvertLocalResponseNormalization(operation, model, data);
133 case V1_2::OperationType::LOGISTIC:
134 return ConvertLogistic(operation, model, data);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +0100135 case V1_2::OperationType::LOG_SOFTMAX:
136 return ConvertLogSoftmax(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100137 case V1_2::OperationType::LSTM:
138 return ConvertLstm(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100139 case V1_2::OperationType::MAX_POOL_2D:
140 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100141 case V1_2::OperationType::MAXIMUM:
142 return ConvertMaximum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100143 case V1_2::OperationType::MEAN:
144 return ConvertMean(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100145 case V1_2::OperationType::MINIMUM:
146 return ConvertMinimum(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100147 case V1_2::OperationType::MUL:
148 return ConvertMul(operation, model, data);
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000149 case V1_2::OperationType::NOT_EQUAL:
150 return ConvertComparison(operation, model, data, ComparisonOperation::NotEqual);
Mike Kelly3c673942019-07-25 09:26:06 +0100151 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100152 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100153 case V1_2::OperationType::PAD_V2:
154 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100155 case V1_2::OperationType::PRELU:
156 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +0100157 case V1_2::OperationType::QUANTIZE:
158 return ConvertQuantize(operation, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +0100159 case V1_2::OperationType::QUANTIZED_16BIT_LSTM:
160 return ConvertQuantizedLstm(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100161 case V1_2::OperationType::RELU:
162 return ConvertReLu(operation, model, data);
163 case V1_2::OperationType::RELU1:
164 return ConvertReLu1(operation, model, data);
165 case V1_2::OperationType::RELU6:
166 return ConvertReLu6(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100167 case V1_2::OperationType::RESHAPE:
168 return ConvertReshape(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100169 case V1_2::OperationType::RESIZE_BILINEAR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100170 return ConvertResize(operation, model, data, ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100171 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100172 return ConvertResize(operation, model, data, ResizeMethod::NearestNeighbor);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +0100173 case V1_2::OperationType::RSQRT:
174 return ConvertRsqrt(operation, model, data);
Sadik Armagan701d9a02019-09-04 15:16:18 +0100175 case V1_2::OperationType::SQRT:
176 return ConvertSqrt(operation, model, data);
Mike Kelly46272802019-08-14 17:00:48 +0100177 case V1_2::OperationType::SQUEEZE:
178 return ConvertSqueeze(operation, model, data);
179 case V1_2::OperationType::STRIDED_SLICE:
180 return ConvertStridedSlice(operation, model, data);
181 case V1_2::OperationType::TRANSPOSE:
182 return ConvertTranspose(operation, model, data);
David Monahan613b49c2019-06-27 11:37:47 +0100183 case V1_2::OperationType::TRANSPOSE_CONV_2D:
Aron Virginas-Tar8b991682019-07-31 12:54:59 +0100184 return ConvertTransposeConv2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +0100185 case V1_2::OperationType::SOFTMAX:
186 return ConvertSoftmax(operation, model, data);
Finn Williamsd74c5052019-07-30 17:06:00 +0100187 case V1_2::OperationType::SPACE_TO_BATCH_ND :
188 return ConvertSpaceToBatchNd(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100189 case V1_2::OperationType::SPACE_TO_DEPTH:
190 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100191 case V1_2::OperationType::SUB:
192 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100193 case V1_2::OperationType::TANH:
194 return ConvertTanH(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100195 default:
196 return Fail("%s: Operation type %s not supported in ArmnnDriver",
197 __func__, toString(operation.type).c_str());
198 }
199}
200
Kevin May407718f2019-09-09 14:46:41 +0100201bool HalPolicy::ConvertAbs(const Operation& operation, const Model& model, ConversionData& data)
202{
203 ALOGV("hal_1_2::HalPolicy::ConvertAbs()");
204 return ::ConvertAbs<hal_1_2::HalPolicy>(operation, model, data);
205}
206
Mike Kelly46272802019-08-14 17:00:48 +0100207bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
208{
209 ALOGV("hal_1_2::HalPolicy::ConvertAdd()");
210 return ::ConvertAdd<hal_1_2::HalPolicy>(operation, model, data);
211}
212
Sadik Armagan15d63e22019-07-26 16:59:35 +0100213bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
214{
215 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100216 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Average, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100217}
218
Finn Williams23b87b32019-07-30 11:44:05 +0100219bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
220{
221 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
222 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
223}
224
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +0000225bool HalPolicy::ConvertComparison(const Operation& operation,
226 const Model& model,
227 ConversionData& data,
228 ComparisonOperation comparisonOperation)
229{
230 ALOGV("hal_1_2::HalPolicy::ConvertComparison()");
231 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
232
233 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
234 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
235
236 if (!(input0.IsValid() && input1.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
247 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
248 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
249 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
250
251 if (IsDynamicTensor(outputInfo))
252 {
253 return Fail("%s: Dynamic output tensors are not supported", __func__);
254 }
255
256 ComparisonDescriptor descriptor(comparisonOperation);
257
258 bool isSupported = false;
259 FORWARD_LAYER_SUPPORT_FUNC(__func__,
260 IsComparisonSupported,
261 data.m_Backends,
262 isSupported,
263 inputInfo0,
264 inputInfo1,
265 outputInfo,
266 descriptor);
267
268 if (!isSupported)
269 {
270 return false;
271 }
272
273 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
274 assert(layer != nullptr);
275
276 input0.Connect(layer->GetInputSlot(0));
277 input1.Connect(layer->GetInputSlot(1));
278
279 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
280}
281
Mike Kellyb8805202019-07-31 17:25:43 +0100282bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
283{
284 ALOGV("hal_1_2::HalPolicy::ConvertConcatenation()");
285 return ::ConvertConcatenation<hal_1_2::HalPolicy>(operation, model, data);
286}
287
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100288bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
289{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100290 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
291
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100292 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
293 if (!input.IsValid())
294 {
295 return Fail("%s: Operation has invalid inputs", __func__);
296 }
297
298 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
299 if (!output)
300 {
301 return Fail("%s: Could not read output 0", __func__);
302 }
303
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100304 const TensorInfo& inputInfo = input.GetTensorInfo();
305 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100306
307 if (IsDynamicTensor(outputInfo))
308 {
309 return Fail("%s: Dynamic output tensors are not supported", __func__);
310 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100311
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100312 Convolution2dDescriptor desc;
313 desc.m_DataLayout = DataLayout::NHWC;
Mike Kellye1d60bb2019-07-11 11:44:52 +0100314
315 // Determine whether padding is implicit or explicit
316 bool implicitPadding = operation.inputs.size() == 7 ||
317 (operation.inputs.size() >= 8 &&
318 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
319
320 if (implicitPadding)
321 {
322 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
323 }
324 else if (operation.inputs.size() >= 10)
325 {
326 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
327 }
328
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100329 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
Mike Kellye1d60bb2019-07-11 11:44:52 +0100330
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100331 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100332 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
333 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
334 // the DataLayout is NCHW
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100335 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
Mike Kellye1d60bb2019-07-11 11:44:52 +0100336 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
337 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100338 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100339 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100340
341 if (!weightsPin.IsValid())
342 {
343 return Fail("%s: Operation has invalid weights", __func__);
344 }
345
346 if (!biasPin.IsValid())
347 {
348 return Fail("%s: Operation has invalid biases", __func__);
349 }
350
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100351 ConstTensor weights = weightsPin.GetConstTensor();
352 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100353 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
354
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100355 ActivationFn activation;
356
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100357 if (implicitPadding)
358 {
359 android::nn::PaddingScheme paddingScheme;
360 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
361 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
362 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
363 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
364 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
365 {
366 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
367 }
368
Mike Kellye1d60bb2019-07-11 11:44:52 +0100369 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
370 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
371 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
372 const uint32_t kernelX = weights.GetShape()[widthIndex];
373 const uint32_t kernelY = weights.GetShape()[heightIndex];
374 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
375 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100376
Mike Kelly86b36d42019-07-12 16:39:33 +0100377 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
378 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100379
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100380 }
381 else if (operation.inputs.size() >= 10)
382 {
383 // explicit padding
384 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
385 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
386 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
387 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
388 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
389 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
390 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
391 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
392 {
393 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
394 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100395 }
396 else
397 {
398 return Fail("%s: Unsupported number of operation inputs", __func__);
399 }
400
401 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100402 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100403
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100404 bool isSupported = false;
405 FORWARD_LAYER_SUPPORT_FUNC(__func__,
406 IsConvolution2dSupported,
407 data.m_Backends,
408 isSupported,
409 inputInfo,
410 outputInfo,
411 desc,
412 weights.GetInfo(),
413 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100414
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100415 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100416 {
417 return false;
418 }
419
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100420 IConnectableLayer* startLayer =
421 data.m_Network->AddConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100422
423 if (!startLayer)
424 {
425 return Fail("%s: AddConvolution2dLayer failed", __func__);
426 }
427
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100428 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100429
430 if (!endLayer)
431 {
432 return Fail("%s: ProcessActivation failed", __func__);
433 }
434
435 input.Connect(startLayer->GetInputSlot(0));
436
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100437 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100438}
439
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +0100440bool HalPolicy::ConvertDepthToSpace(const Operation& operation, const Model& model, ConversionData& data)
441{
442 ALOGV("hal_1_2::HalPolicy::ConvertDepthToSpace()");
443 return ::ConvertDepthToSpace<hal_1_2::HalPolicy>(operation, model, data);
444}
445
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100446bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
447{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100448 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
449
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100450 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
451
452 if (!input.IsValid())
453 {
454 return Fail("%s: Operation has invalid inputs", __func__);
455 }
456
457 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
458
459 if (!output)
460 {
461 return Fail("%s: Could not read output 0", __func__);
462 }
463
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100464 const TensorInfo& inputInfo = input.GetTensorInfo();
465 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100466
467 if (IsDynamicTensor(outputInfo))
468 {
469 return Fail("%s: Dynamic output tensors are not supported", __func__);
470 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100471
472 // ArmNN does not currently support non-fixed weights or bias
473 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
474 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
475
476 if (weightsOperand == nullptr)
477 {
478 return Fail("%s: Operand is invalid", __func__);
479 }
Teresa Charlin3b959602019-10-31 17:05:47 +0000480 if ( weightsOperand->dimensions[0] != 1)
481 {
482 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
483 __func__, weightsOperand->dimensions[0] );
484 }
485
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100486 DepthwiseConvolution2dDescriptor desc;
487 desc.m_DataLayout = DataLayout::NHWC;
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100488
489 // Determine whether padding is implicit or explicit
490 bool implicitPadding = operation.inputs.size() == 8 ||
491 (operation.inputs.size() >= 9 &&
492 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
493
494 // Look ahead to find the optional DataLayout, if present
495 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
496 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
497
498 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
499 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
500 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
501 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
502
503 // Reinterpret weight data as [ H, W, I, M ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100504 TensorShape weightsShape({ weightsOperand->dimensions[1],
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100505 weightsOperand->dimensions[2],
506 inputInfo.GetShape()[channelsIndex],
507 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
508
509 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100510 const PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100511
512 const ConstTensorPin weightsPin =
513 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
514 1,
515 model,
516 data,
517 HWIMToMIHW,
518 &weightsShape);
519
520 // Bias is a 1D tensor
521 const ConstTensorPin biasPin =
522 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
523
524 if (!weightsPin.IsValid())
525 {
526 return Fail("%s: Operation has invalid weights", __func__);
527 }
528
529 if (!biasPin.IsValid())
530 {
531 return Fail("%s: Operation has invalid biases", __func__);
532 }
533
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100534 ConstTensor weights = weightsPin.GetConstTensor();
535 ConstTensor bias = biasPin.GetConstTensor();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100536 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
537
538 ActivationFn activation;
539
540 if (implicitPadding)
541 {
542 android::nn::PaddingScheme paddingScheme;
543 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
544 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
545 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
546 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
547 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
548 {
549 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
550 }
551
552 const uint32_t kernelX = weights.GetShape()[3];
553 const uint32_t kernelY = weights.GetShape()[2];
554 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
555 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
556
Mike Kelly86b36d42019-07-12 16:39:33 +0100557 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
558 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100559 }
560 else if (operation.inputs.size() >= 11)
561 {
562 // explicit padding
563 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
564 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
565 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
566 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
567 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
568 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
569 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
570 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
571 {
572 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
573 }
574 }
575 else
576 {
577 return Fail("%s: Unsupported number of operation inputs", __func__);
578 }
579
580 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100581 Optional<TensorInfo> biases(bias.GetInfo());
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100582
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100583 bool isSupported = false;
584 FORWARD_LAYER_SUPPORT_FUNC(__func__,
585 IsDepthwiseConvolutionSupported,
586 data.m_Backends,
587 isSupported,
588 inputInfo,
589 outputInfo,
590 desc,
591 weights.GetInfo(),
592 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100593
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100594 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100595 {
596 return false;
597 }
598
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100599 IConnectableLayer* startLayer =
600 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100601
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100602 if (!startLayer)
603 {
604 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
605 }
606
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100607 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100608 if (!endLayer)
609 {
610 return Fail("%s: ProcessActivation failed", __func__);
611 }
612
613 input.Connect(startLayer->GetInputSlot(0));
614
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100615 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100616}
617
Mike Kelly46272802019-08-14 17:00:48 +0100618bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
619{
620 ALOGV("hal_1_2::HalPolicy::ConvertDequantize()");
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000621
622 if (IsQSymmDequantizeForWeights(operation, model))
623 {
624 // NOTE: QSymm8 weights are dequantized internally by the driver,
625 // therefore this type of Dequantize is implicitly supported
626 return true;
627 }
628
Mike Kelly46272802019-08-14 17:00:48 +0100629 return ::ConvertDequantize<hal_1_2::HalPolicy>(operation, model, data);
630}
631
632bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
633{
634 ALOGV("hal_1_2::HalPolicy::ConvertDiv()");
635 return ::ConvertDiv<hal_1_2::HalPolicy>(operation, model, data);
636}
637
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100638bool HalPolicy::ConvertExpandDims(const Operation& operation, const Model& model, ConversionData& data)
639{
640 ALOGV("hal_1_2::HalPolicy::ConvertExpandDims()");
641
642 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
643
644 if (!input.IsValid())
645 {
646 return Fail("%s: Operation has invalid input", __func__);
647 }
648
649 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
650 if (!output)
651 {
652 return Fail("%s: Operation has invalid output", __func__);
653 }
654
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100655 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100656 if (IsDynamicTensor(outputInfo))
657 {
658 return Fail("%s: Dynamic output tensors are not supported", __func__);
659 }
660
661 int32_t axis;
662 if (!GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, axis, model, data))
663 {
664 return Fail("%s: failed to get axis input value", __func__);
665 }
666
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100667 TensorShape targetShape;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100668
669 try
670 {
671 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
672 }
673 catch (const std::exception &e)
674 {
675 return Fail("%s: %s", __func__, e.what());
676 }
677
678 if (targetShape != outputInfo.GetShape())
679 {
680 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
681 }
682
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100683 ReshapeDescriptor reshapeDescriptor;
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100684 reshapeDescriptor.m_TargetShape = targetShape;
685
686 bool isSupported = false;
687 FORWARD_LAYER_SUPPORT_FUNC(__func__,
688 IsReshapeSupported,
689 data.m_Backends,
690 isSupported,
691 input.GetTensorInfo(),
692 reshapeDescriptor);
693
694 if (!isSupported)
695 {
696 return false;
697 }
698
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100699 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat85f96542019-09-12 16:26:29 +0100700 assert(layer != nullptr);
701 input.Connect(layer->GetInputSlot(0));
702
703 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
704}
705
Mike Kelly46272802019-08-14 17:00:48 +0100706bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
707{
708 ALOGV("hal_1_2::HalPolicy::ConvertFloor()");
709 return ::ConvertFloor<hal_1_2::HalPolicy>(operation, model, data);
710}
711
712bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
713{
714 ALOGV("hal_1_2::HalPolicy::ConvertFullyConnected()");
715 return ::ConvertFullyConnected<hal_1_2::HalPolicy>(operation, model, data);
716}
717
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100718bool HalPolicy::ConvertGroupedConv2d(const Operation& operation, const Model& model, ConversionData& data)
719{
720 ALOGV("hal_1_2::HalPolicy::ConvertGroupedConv2d()");
721
722 //
723 // Parse data
724 //
725 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
726 if (!input.IsValid())
727 {
728 return Fail("%s: Operation has invalid inputs", __func__);
729 }
730 const TensorInfo& inputInfo = input.GetTensorInfo();
731
732 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
733 if (!output)
734 {
735 return Fail("%s: Could not read output 0", __func__);
736 }
737 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
738 if (IsDynamicTensor(outputInfo))
739 {
740 return Fail("%s: Dynamic output tensors are not supported", __func__);
741 }
742
743 // Look ahead to determine data layout
744 DataLayout dataLayout = DataLayout::NHWC;
745 if (operation.inputs.size() == 12)
746 {
747 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 11, model, data);
748 }
749 else
750 {
751 dataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
752 }
753
754 // NOTE:
755 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
756 // but Arm NN expects the filter's height and width indices to match the input's height and
757 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
758 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
759 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
760 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, ohwiToOihw) :
761 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
762 const ConstTensorPin biasesPin =
763 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
764 if (!weightsPin.IsValid() || !biasesPin.IsValid())
765 {
766 return Fail("%s: Operation has invalid inputs", __func__);
767 }
768
769 ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000770 ConstTensor biases = biasesPin.GetConstTensor();
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100771 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
772
773 const TensorShape& inputShape = inputInfo.GetShape();
774 const TensorShape& outputShape = outputInfo.GetShape();
775 const TensorShape& weightsShape = weights.GetShape();
776 const TensorShape& biasesShape = biases.GetShape();
777
778 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
779 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
780 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
781 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
782
783 Convolution2dDescriptor desc;
784 desc.m_DataLayout = dataLayout;
785 desc.m_BiasEnabled = true;
786
787 int numGroups;
788 ActivationFn activation;
789
790 if (operation.inputs.size() == 12)
791 {
792 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
793 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
794 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
795 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
796 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
797 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
798 !GetInputScalar<hal_1_2::HalPolicy>(operation, 9, OperandType::INT32, numGroups, model, data) ||
799 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data))
800 {
801 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
802 }
803
804 }
805 else if (operation.inputs.size() == 9)
806 {
807 android::nn::PaddingScheme paddingScheme;
808 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
809 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
810 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
811 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, numGroups, model, data) ||
812 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
813 {
814 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
815 }
816
817 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
818 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
819
820 const uint32_t kernelX = weightsShape[widthIndex];
821 const uint32_t kernelY = weightsShape[heightIndex];
822
823 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
824 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
825 }
826 else
827 {
828 return Fail("%s: Unsupported number of operation inputs", __func__);
829 }
830
831 const unsigned int outputChannels = outputShape[channelsIndex];
832
833 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
834 const unsigned int channelMultiplier = outputChannels / numGroups;
835
836 //
837 // Validate all relevant inputs
838 //
839 if (numGroups <= 0)
840 {
841 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
842 }
843
844 if (outputChannels % numGroups != 0u)
845 {
846 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
847 }
848
849 //
850 // Set up Splitter layer
851 //
852 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
853 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
854
855 TensorInfo splitterOutputInfo(4,
856 splitterDimSizes,
857 inputInfo.GetDataType(),
858 inputInfo.GetQuantizationScale(),
859 inputInfo.GetQuantizationOffset());
860
861 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
862
863 ViewsDescriptor splitterDesc(numGroups);
864 for (unsigned int group = 0u; group < numGroups; ++group)
865 {
866 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
867 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
868 {
869 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
870 }
871 }
872
873 bool isSupported = false;
874 FORWARD_LAYER_SUPPORT_FUNC(__func__,
875 IsSplitterSupported,
876 data.m_Backends,
877 isSupported,
878 inputInfo,
879 splitterOutputInfos,
880 splitterDesc);
881 if (!isSupported)
882 {
883 return false;
884 }
885
886 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
887 if (!splitterLayer)
888 {
889 return Fail("%s: Failed to add SplitterLayer", __func__);
890 }
891
892 input.Connect(splitterLayer->GetInputSlot(0));
893 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
894 {
895 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
896 }
897
898 //
899 // Set up Convolution2d layers for each group
900 //
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000901
902 // Set up group tensor shapes
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100903 TensorShape groupInputShape(inputShape);
904 groupInputShape[channelsIndex] = channelsPerGroup;
905
906 TensorShape groupOutputShape(outputShape);
907 groupOutputShape[channelsIndex] = 1;
908
909 TensorShape groupWeightsShape(weightsShape);
910 groupWeightsShape[0] /= channelMultiplier * numGroups;
911
912 TensorShape groupBiasesShape({ 1 });
913
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000914 // Set up group tensor infos
915 TensorInfo groupInputInfo(inputInfo);
916 groupInputInfo.SetShape(groupInputShape);
917
918 const TensorInfo& weightsInfo = weights.GetInfo();
919 TensorInfo groupWeightsInfo(weightsInfo);
920 groupWeightsInfo.SetShape(groupWeightsShape);
921
922 const TensorInfo& biasesInfo = biases.GetInfo();
923 TensorInfo groupBiasesInfo(biasesInfo);
924 groupBiasesInfo.SetShape(groupBiasesShape);
925
926 TensorInfo groupOutputInfo(outputInfo);
927 groupOutputInfo.SetShape(groupOutputShape);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100928
929 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
930 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
931
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000932 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100933 for (unsigned int group = 0u; group < numGroups; ++group)
934 {
935 for (unsigned int m = 0u; m < channelMultiplier; ++m)
936 {
937 auto index = group * channelMultiplier + m;
938
939 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
940 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
941
Aron Virginas-Tar60a346b2019-11-07 14:49:26 +0000942 if (weightsInfo.HasPerAxisQuantization())
943 {
944 // Extract per-axis quantization scales for group weights
945 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
946 groupWeightsInfo.SetQuantizationScales(
947 std::vector<float>(weightsQuantScales.begin() + index,
948 weightsQuantScales.begin() + index + groupWeightsShape[0]));
949
950 // Extract per-axis quantization scales for group biases
951 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
952 groupBiasesInfo.SetQuantizationScales(
953 std::vector<float>(biasesQuantScales.begin() + index,
954 biasesQuantScales.begin() + index + groupWeightsShape[0]));
955 }
956
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100957 // Extract weights and biases data for current group convolution
958 ConstTensor groupWeights(groupWeightsInfo,
959 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
960 weightsDataOffset));
961 ConstTensor groupBiases(groupBiasesInfo,
962 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
963 biasesDataOffset));
964
965 isSupported = false;
966 FORWARD_LAYER_SUPPORT_FUNC(__func__,
967 IsConvolution2dSupported,
968 data.m_Backends,
969 isSupported,
970 groupInputInfo,
971 groupOutputInfo,
972 desc,
973 groupWeightsInfo,
974 Optional<TensorInfo>(groupBiasesInfo));
975 if (!isSupported)
976 {
977 return false;
978 }
979
980 IConnectableLayer *convLayer =
981 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
982 if (!convLayer)
983 {
984 return Fail("%s: AddConvolution2dLayer failed", __func__);
985 }
986
987 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
988 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
989
990 convLayers[index] = convLayer;
991 }
992 }
993
994 //
995 // Set up Concat layer
996 //
997 ConcatDescriptor concatDescriptor(outputInfo.GetShape()[channelsIndex]);
998 for (unsigned int group = 0u; group < numGroups; ++group)
999 {
1000 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1001 {
1002 auto index = group * channelMultiplier + m;
1003 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1004 concatDescriptor.SetConcatAxis(channelsIndex);
1005 }
1006 }
1007
1008 isSupported = false;
1009 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1010 IsConcatSupported,
1011 data.m_Backends,
1012 isSupported,
1013 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1014 outputInfo,
1015 concatDescriptor);
1016 if (!isSupported)
1017 {
1018 return false;
1019 }
1020
1021 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1022 if (!concatLayer)
1023 {
1024 return Fail("%s: AddConcatLayer failed", __func__);
1025 }
1026
1027 for (unsigned int group = 0u; group < numGroups; ++group)
1028 {
1029 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1030 {
1031 auto index = group * channelMultiplier + m;
1032 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1033 }
1034 }
1035 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1036
1037 //
1038 // Set up Activation layer (if it is set)
1039 //
1040 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, concatLayer, data);
1041 if (!endLayer)
1042 {
1043 return Fail("%s: ProcessActivation failed", __func__);
1044 }
1045
1046 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1047}
1048
Aron Virginas-Tara2a73802019-10-09 15:30:40 +01001049bool HalPolicy::ConvertInstanceNormalization(const Operation& operation, const Model& model, ConversionData& data)
1050{
1051 ALOGV("hal_1_2::HalPolicy::ConvertInstanceNormalization()");
1052
1053 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1054 if (!input.IsValid())
1055 {
1056 return Fail("%s: Operation has an invalid input 0", __func__);
1057 }
1058
1059 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1060 if (!output)
1061 {
1062 return Fail("%s: Operation has an invalid output", __func__);
1063 }
1064
1065 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1066 if (IsDynamicTensor(outputInfo))
1067 {
1068 return Fail("%s: Dynamic output tensors are not supported", __func__);
1069 }
1070
1071 // Determine data type of input tensor
1072 OperandType inputType;
1073 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1074 {
1075 return Fail("%s: Operation has invalid inputs", __func__);
1076 }
1077
1078 InstanceNormalizationDescriptor desc;
1079
1080 // Read gamma, beta & epsilon
1081 if (inputType == OperandType::TENSOR_FLOAT16)
1082 {
1083 Half fp16Gamma;
1084 Half fp16Beta;
1085 Half fp16Epsilon;
1086
1087 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Gamma, model, data) ||
1088 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT16, fp16Beta, model, data) ||
1089 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT16, fp16Epsilon, model, data))
1090 {
1091 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1092 }
1093
1094 desc.m_Gamma = static_cast<float>(fp16Gamma);
1095 desc.m_Beta = static_cast<float>(fp16Beta);
1096 desc.m_Eps = static_cast<float>(fp16Epsilon);
1097 }
1098 else if (inputType == OperandType::TENSOR_FLOAT32)
1099 {
1100 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, desc.m_Gamma, model, data) ||
1101 !GetInputScalar<hal_1_2::HalPolicy>(operation, 2, OperandType::FLOAT32, desc.m_Beta, model, data) ||
1102 !GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::FLOAT32, desc.m_Eps, model, data))
1103 {
1104 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1105 }
1106 }
1107 else
1108 {
1109 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1110 }
1111
1112 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 4, model, data);
1113
1114 bool isSupported = false;
1115 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1116 IsInstanceNormalizationSupported,
1117 data.m_Backends,
1118 isSupported,
1119 input.GetTensorInfo(),
1120 outputInfo,
1121 desc);
1122 if (!isSupported)
1123 {
1124 return false;
1125 }
1126
1127 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1128 input.Connect(layer->GetInputSlot(0));
1129
1130 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1131}
1132
Mike Kelly46272802019-08-14 17:00:48 +01001133bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1134{
1135 ALOGV("hal_1_2::HalPolicy::ConvertL2Normalization()");
1136 return ::ConvertL2Normalization<hal_1_2::HalPolicy>(operation, model, data);
1137}
1138
Sadik Armagan15d63e22019-07-26 16:59:35 +01001139bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1140{
1141 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001142 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::L2, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001143}
1144
Mike Kelly46272802019-08-14 17:00:48 +01001145bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
1146 const Model& model,
1147 ConversionData& data)
1148{
1149 ALOGV("hal_1_2::HalPolicy::ConvertLocalResponseNormalization()");
1150 return ::ConvertLocalResponseNormalization<hal_1_2::HalPolicy>(operation, model, data);
1151}
1152
1153bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
1154{
1155 ALOGV("hal_1_2::HalPolicy::ConvertLogistic()");
1156 return ::ConvertLogistic<hal_1_2::HalPolicy>(operation, model, data);
1157}
1158
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001159bool HalPolicy::ConvertLogSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1160{
1161 ALOGV("hal_1_2::HalPolicy::ConvertLogSoftmax()");
1162
1163 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1164 if (!input.IsValid())
1165 {
1166 return Fail("%s: Failed to read input 0", __func__);
1167 }
1168
1169 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1170 if (!output)
1171 {
1172 return Fail("%s: Failed to read output", __func__);
1173 }
1174
1175 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1176 if (IsDynamicTensor(outputInfo))
1177 {
1178 return Fail("%s: Dynamic output tensors are not supported", __func__);
1179 }
1180
1181 // Determine data type of input tensor
1182 OperandType inputType;
1183 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, inputType))
1184 {
1185 return Fail("%s: Operation has invalid inputs", __func__);
1186 }
1187
1188 LogSoftmaxDescriptor descriptor;
1189
1190 // Read beta
1191 if (inputType == OperandType::TENSOR_FLOAT16)
1192 {
1193 Half fp16Beta;
1194 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT16, fp16Beta, model, data))
1195 {
1196 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1197 }
1198
1199 descriptor.m_Beta = static_cast<float>(fp16Beta);
1200 }
1201 else if (inputType == OperandType::TENSOR_FLOAT32)
1202 {
1203 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::FLOAT32, descriptor.m_Beta, model, data))
1204 {
1205 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1206 }
1207 }
1208 else
1209 {
1210 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1211 }
1212
1213 // Read axis
1214 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1215 {
1216 return Fail("%s: Failed to read input 2", __func__);
1217 }
1218
1219 bool isSupported = false;
1220 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1221 IsLogSoftmaxSupported,
1222 data.m_Backends,
1223 isSupported,
1224 input.GetTensorInfo(),
1225 outputInfo,
1226 descriptor);
1227 if (!isSupported)
1228 {
1229 return false;
1230 }
1231
Aron Virginas-Tar3e0982b2019-10-29 14:25:09 +00001232 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Aron Virginas-Tar75e67792019-10-15 13:33:03 +01001233 if (!layer)
1234 {
1235 return Fail("%s: AddLogSoftmaxLayer() returned nullptr", __func__);
1236 }
1237
1238 input.Connect(layer->GetInputSlot(0));
1239
1240 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1241}
1242
Sadik Armagan15d63e22019-07-26 16:59:35 +01001243bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1244{
1245 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001246 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, PoolingAlgorithm::Max, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001247}
1248
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001249bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
1250{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001251 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
1252
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001253 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1254 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1255
1256 if (!input0.IsValid() || !input1.IsValid())
1257 {
1258 return Fail("%s: Operation has invalid inputs", __func__);
1259 }
1260
1261 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1262 if (!outputOperand)
1263 {
1264 return Fail("%s: Could not read output", __func__);
1265 }
1266
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001267 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001268 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001269 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001270 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001271 }
1272
Aron Virginas-Tard7593232019-07-16 13:17:06 +01001273 bool isSupported = false;
1274 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1275 IsMaximumSupported,
1276 data.m_Backends,
1277 isSupported,
1278 input0.GetTensorInfo(),
1279 input1.GetTensorInfo(),
1280 outInfo);
1281
1282 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001283 {
1284 return false;
1285 }
1286
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001287 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001288 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001289 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1290 if (!isReshapeSupported)
1291 {
1292 return false;
1293 }
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001294
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001295 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +01001296}
1297
Mike Kelly46272802019-08-14 17:00:48 +01001298bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
1299{
1300 ALOGV("hal_1_2::HalPolicy::ConvertMean()");
1301 return ::ConvertMean<hal_1_2::HalPolicy>(operation, model, data);
1302}
1303
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001304bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
1305{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001306 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
1307
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001308 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1309 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1310
1311 if (!input0.IsValid() || !input1.IsValid())
1312 {
1313 return Fail("%s: Operation has invalid inputs", __func__);
1314 }
1315
1316 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1317 if (!output)
1318 {
1319 return Fail("%s: Could not read output 0", __func__);
1320 }
1321
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001322 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001323 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001324 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001325 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001326 }
1327
1328 bool isSupported = false;
1329 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1330 IsMinimumSupported,
1331 data.m_Backends,
1332 isSupported,
1333 input0.GetTensorInfo(),
1334 input1.GetTensorInfo(),
1335 outputInfo);
1336
1337 if (!isSupported)
1338 {
1339 return false;
1340 }
1341
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001342 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001343 assert(layer != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001344 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1345 if (!isReshapeSupported)
1346 {
1347 return false;
1348 }
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001349
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001350 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +01001351}
1352
Mike Kelly46272802019-08-14 17:00:48 +01001353bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1354{
1355 ALOGV("hal_1_2::HalPolicy::ConvertMul()");
1356 return ::ConvertMul<hal_1_2::HalPolicy>(operation, model, data);
1357}
1358
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +01001359bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
1360{
1361 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
1362 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
1363}
1364
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001365bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
1366{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001367 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
1368
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001369 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1370 if (!input.IsValid())
1371 {
1372 return Fail("%s: Could not read input 0", __func__);
1373 }
1374
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001375 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1376 if (!output)
1377 {
1378 return Fail("%s: Could not read output", __func__);
1379 }
1380
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001381 const TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001382 unsigned int rank = inputInfo.GetNumDimensions();
1383
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001384 PadDescriptor descriptor;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001385 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
1386 {
1387 return Fail("%s: Could not convert paddings", __func__);
1388 }
1389
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001390 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001391 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001392 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001393 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +01001394 }
1395
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001396 // Determine type of padding value
1397 OperandType operandType0;
1398 OperandType operandType2;
1399
1400 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
1401 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1402 {
1403 return Fail("%s: Operation has invalid inputs", __func__);
1404 }
1405
1406 // Read value to use for padding
1407 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
1408 {
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001409 Half f16PadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001410 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1411 {
1412 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1413 }
1414
1415 descriptor.m_PadValue = f16PadValue;
1416 }
1417 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
1418 {
1419 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1420 {
1421 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1422 }
1423 }
1424 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
1425 {
Mike Kelly3c673942019-07-25 09:26:06 +01001426 int32_t intPadValue = 0;
1427 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001428 {
1429 return Fail("%s: Could not read input 2 (INT32)", __func__);
1430 }
Mike Kelly3c673942019-07-25 09:26:06 +01001431 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001432 }
1433 else
1434 {
1435 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1436 }
1437
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001438 bool isSupported = false;
1439 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1440 IsPadSupported,
1441 data.m_Backends,
1442 isSupported,
1443 inputInfo,
1444 outputInfo,
1445 descriptor);
1446 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001447 {
1448 return false;
1449 }
1450
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001451 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001452 assert(layer != nullptr);
1453 input.Connect(layer->GetInputSlot(0));
1454 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1455
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001456 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001457}
1458
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001459bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
1460{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001461 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
1462
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001463 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1464 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
1465
1466 if (!input.IsValid() || !alpha.IsValid())
1467 {
1468 return Fail("%s: Operation has invalid inputs", __func__);
1469 }
1470
1471 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1472
1473 if (!output)
1474 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +01001475 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001476 }
1477
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001478 const TensorInfo& inputInfo = input.GetTensorInfo();
1479 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1480 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001481
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001482 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001483 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001484 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001485 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001486
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001487 bool isSupported = false;
1488 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1489 IsPreluSupported,
1490 data.m_Backends,
1491 isSupported,
1492 inputInfo,
1493 alphaInfo,
1494 outputInfo);
1495 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001496 {
1497 return false;
1498 }
1499
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001500 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001501
1502 if (!layer)
1503 {
1504 return Fail("%s: AddPreluLayer failed", __func__);
1505 }
1506
Sadik Armagan64b19b52019-08-19 09:49:58 +01001507 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1508 if (!isReshapeSupported)
1509 {
1510 return false;
1511 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001512
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001513 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001514}
1515
Sadik Armagan5a476a82019-07-30 09:43:18 +01001516bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
1517{
1518 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
1519
1520 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1521 if (!input.IsValid())
1522 {
1523 return Fail("%s: Operation has invalid input", __func__);
1524 }
1525
1526 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1527 if (!outputOperand)
1528 {
1529 return Fail("%s: Operation has invalid outputs", __func__);
1530 }
1531
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001532 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan5a476a82019-07-30 09:43:18 +01001533 if (IsDynamicTensor(outputInfo))
1534 {
1535 return Fail("%s: Dynamic output tensors are not supported", __func__);
1536 }
1537
1538 bool isSupported = false;
1539 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1540 IsQuantizeSupported,
1541 data.m_Backends,
1542 isSupported,
1543 input.GetTensorInfo(),
1544 outputInfo);
1545 if (!isSupported)
1546 {
1547 return false;
1548 }
1549
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001550 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Sadik Armagan5a476a82019-07-30 09:43:18 +01001551 assert(layer != nullptr);
1552 input.Connect(layer->GetInputSlot(0));
1553
1554 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1555}
1556
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001557bool HalPolicy::ConvertQuantizedLstm(const Operation& operation, const Model& model, ConversionData& data)
1558{
1559 ALOGV("hal_1_2::HalPolicy::ConvertQuantizedLstm()");
1560
1561 //Inputs:
1562 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1563 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1564 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1565 if (!input.IsValid())
1566 {
1567 return Fail("%s: Could not read input 0: input", __func__);
1568 }
1569
1570 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1571 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1572 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1573 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 13, model, data);
1574 if (!previousCellStateIn.IsValid())
1575 {
1576 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1577 }
1578
1579 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1580 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1581 // is quantized with a fixed quantization range of -1, 127/128.
1582 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 14, model, data);
1583 if (!previousOutputIn.IsValid())
1584 {
1585 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1586 }
1587
1588 // Get the input tensors:
1589 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1590 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1591 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1592 const ConstTensorPin inputToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001593 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001594
1595 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1596 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1597 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1598 const ConstTensorPin inputToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001599 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001600
1601 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1602 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1603 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1604 const ConstTensorPin inputToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001605 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001606
1607 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1608 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1609 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1610 const ConstTensorPin inputToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001611 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001612
1613 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1614 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1615 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1616 const ConstTensorPin recurrentToInputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001617 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 5, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001618
1619 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1620 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1621 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1622 const ConstTensorPin recurrentToForgetWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001623 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001624
1625 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1626 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1627 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1628 const ConstTensorPin recurrentToCellWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001629 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001630
1631 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1632 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1633 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1634 const ConstTensorPin recurrentToOutputWeightsPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001635 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001636
1637 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1638 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1639 // of input and weights scales and zeroPoint equal to 0.
1640 const ConstTensorPin inputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001641 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 9, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001642
1643 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1644 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1645 // of input and weights scales and zeroPoint equal to 0.
1646 const ConstTensorPin forgetGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001647 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 10, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001648
1649 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1650 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1651 // and weights scales and zeroPoint equal to 0.
1652 const ConstTensorPin cellBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001653 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 11, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001654
1655 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1656 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1657 // of input and weights scales and zeroPoint equal to 0.
1658 const ConstTensorPin outputGateBiasPin =
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001659 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 12, model, data);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001660
1661 if (!inputToInputWeightsPin.IsValid() ||
1662 !inputToForgetWeightsPin.IsValid() ||
1663 !inputToCellWeightsPin.IsValid() ||
1664 !inputToOutputWeightsPin.IsValid() ||
1665 !recurrentToInputWeightsPin.IsValid() ||
1666 !recurrentToForgetWeightsPin.IsValid() ||
1667 !recurrentToCellWeightsPin.IsValid() ||
1668 !recurrentToOutputWeightsPin.IsValid() ||
1669 !inputGateBiasPin.IsValid() ||
1670 !forgetGateBiasPin.IsValid() ||
1671 !cellBiasPin.IsValid() ||
1672 !outputGateBiasPin.IsValid())
1673 {
1674 return Fail("%s: Operation has invalid tensor inputs", __func__);
1675 }
1676
1677 // Outputs:
1678 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1679 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1680 // of -2^4, 2^4 * 32767/32768.
1681 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1682 if (!cellStateOut)
1683 {
1684 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1685 }
1686
1687 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1688 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1689 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1690 if (!output)
1691 {
1692 return Fail("%s: Could not read output 1: output", __func__);
1693 }
1694
1695 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001696 const TensorInfo& inputInfo = input.GetTensorInfo();
1697 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1698 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001699
1700 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001701 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1702 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001703
1704 // Dynamic tensors currently not supported
1705 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1706 {
1707 return Fail("%s: Dynamic output tensors are not supported", __func__);
1708 }
1709
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001710 QuantizedLstmInputParams params;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001711
1712 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1713 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1714 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1715 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1716 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1717 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1718 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1719 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1720 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1721 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1722 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1723 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1724
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001725 QuantizedLstmInputParamsInfo paramsInfo;
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001726 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1727 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1728 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1729 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1730 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1731 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1732 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1733 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1734 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1735 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1736 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1737 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1738
1739 bool isSupported = false;
1740 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1741 IsQuantizedLstmSupported,
1742 data.m_Backends,
1743 isSupported,
1744 inputInfo,
1745 previousCellStateInInfo,
1746 previousOutputInInfo,
1747 cellStateOutInfo,
1748 outputInfo,
1749 paramsInfo);
1750
1751 if (!isSupported)
1752 {
1753 return false;
1754 }
1755
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001756 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001757 input.Connect(layer->GetInputSlot(0));
Ellen Norris-Thompsona3d7fad2019-08-05 14:20:32 +01001758 previousCellStateIn.Connect(layer->GetInputSlot(1));
1759 previousOutputIn.Connect(layer->GetInputSlot(2));
Ellen Norris-Thompson7efb46d2019-07-24 17:39:19 +01001760
1761 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1762 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data));
1763}
1764
Sadik Armagan61113162019-07-25 09:09:40 +01001765bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1766{
1767 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
1768 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
1769}
1770
1771bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1772{
1773 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
1774 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
1775}
1776
1777bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1778{
1779 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
1780 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
1781}
1782
Mike Kelly46272802019-08-14 17:00:48 +01001783bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1784{
1785 ALOGV("hal_1_2::HalPolicy::ConvertReshape()");
1786 return ::ConvertReshape<hal_1_2::HalPolicy>(operation, model, data);
1787}
1788
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001789bool HalPolicy::ConvertResize(const Operation& operation,
1790 const Model& model,
1791 ConversionData& data,
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001792 ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001793{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001794 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
Aron Virginas-Tar7d2ccfd2019-10-29 14:03:51 +00001795 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001796
1797 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001798 if (!input.IsValid())
1799 {
1800 return Fail("%s: Could not read input 0", __func__);
1801 }
1802
1803 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1804 if (!output)
1805 {
1806 return Fail("%s: Could not read output 0", __func__);
1807 }
1808
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001809 const TensorInfo& inputInfo = input.GetTensorInfo();
1810 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001811
1812 if (IsDynamicTensor(outputInfo))
1813 {
1814 return Fail("%s: Dynamic output tensors are not supported", __func__);
1815 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001816
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001817 ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +01001818 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001819 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
1820
1821 OperandType operandType1;
1822 OperandType operandType2;
1823
1824 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
1825 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
1826 {
1827 return Fail("%s: Operation has invalid inputs", __func__);
1828 }
1829
1830 if (operandType1 != operandType2)
1831 {
1832 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
1833 }
1834
1835 if (operandType1 == OperandType::INT32)
1836 {
1837 // Case 1: resizing by shape
1838 int32_t targetWidth = 0;
1839 int32_t targetHeight = 0;
1840
1841 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
1842 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
1843 {
1844 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
1845 }
1846
1847 if (targetWidth < 0 || targetHeight < 0)
1848 {
1849 return Fail("%s: Operation has invalid inputs for resizing by shape. "
1850 "Target width/height cannot be < 0", __func__);
1851 }
1852
1853 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +01001854 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001855 }
1856 else if (operandType1 == OperandType::FLOAT32)
1857 {
1858 // Case 2: resizing by scale
1859 float widthScale = 1.0f;
1860 float heightScale = 1.0f;
1861
1862 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
1863 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
1864 {
1865 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
1866 }
1867
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001868 const TensorShape& inputShape = inputInfo.GetShape();
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001869 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
1870
1871 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
1872 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
1873
1874 descriptor.m_TargetWidth = std::floor(width * widthScale);
1875 descriptor.m_TargetHeight = std::floor(height * heightScale);
1876 }
1877 else
1878 {
1879 // NOTE: FLOAT16 scales are not supported
1880 return false;
1881 }
1882
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001883 bool isSupported = false;
1884 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1885 IsResizeSupported,
1886 data.m_Backends,
1887 isSupported,
1888 inputInfo,
1889 outputInfo,
1890 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +01001891
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001892 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001893 {
1894 return false;
1895 }
1896
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001897 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001898
1899 assert(layer != nullptr);
1900
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001901 input.Connect(layer->GetInputSlot(0));
1902
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001903 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +01001904}
1905
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001906bool HalPolicy::ConvertRsqrt(const Operation& operation, const Model& model, ConversionData& data)
1907{
1908 ALOGV("hal_1_2::HalPolicy::ConvertRsqrt()");
1909
1910 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1911 if (!input.IsValid())
1912 {
1913 return Fail("%s: Operation has invalid input", __func__);
1914 }
1915
1916 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1917 if (!output)
1918 {
1919 return Fail("%s: Could not read output 0", __func__);
1920 }
1921
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001922 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001923 if (IsDynamicTensor(outputInfo))
1924 {
1925 return Fail("%s: Dynamic output tensors are not supported", __func__);
1926 }
1927
1928 bool isSupported = false;
1929 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1930 IsRsqrtSupported,
1931 data.m_Backends,
1932 isSupported,
1933 input.GetTensorInfo(),
1934 outputInfo);
1935
1936 if (!isSupported)
1937 {
1938 return false;
1939 }
1940
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001941 IConnectableLayer* const layer = data.m_Network->AddRsqrtLayer();
Aron Virginas-Tarfa6544e2019-09-10 14:42:22 +01001942 assert(layer != nullptr);
1943 input.Connect(layer->GetInputSlot(0));
1944
1945 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
1946}
1947
Finn Williamsd74c5052019-07-30 17:06:00 +01001948bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data)
1949{
1950 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToBatchNd()");
1951 return ::ConvertSpaceToBatchNd<hal_1_2::HalPolicy>(operation, model, data);
1952}
1953
Keith Davisa6bc52f2019-06-26 09:39:49 +01001954bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1955{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001956 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +01001957
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001958 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01001959 if (!input.IsValid() )
1960 {
1961 return Fail("%s: Operation has invalid inputs", __func__);
1962 }
1963
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001964 const TensorInfo& inputInfo = input.GetTensorInfo();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001965 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +01001966 if (rank != 4)
1967 {
1968 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1969 }
1970
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001971 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1972 if (!output)
1973 {
1974 return Fail("%s: Could not read output 0", __func__);
1975 }
1976
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001977 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001978 if (IsDynamicTensor(outputInfo))
1979 {
1980 return Fail("%s: Dynamic output tensors are not supported", __func__);
1981 }
1982
Teresa Charlin8f6429d2019-10-01 13:10:15 +01001983 SpaceToDepthDescriptor desc;
Keith Davisa6bc52f2019-06-26 09:39:49 +01001984
1985 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1986
1987 if (desc.m_BlockSize <= 1)
1988 {
1989 return Fail("%s: Block size must be at least 1 in all dimensions");
1990 }
1991
1992 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
1993
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001994 bool isSupported = false;
1995 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1996 IsSpaceToDepthSupported,
1997 data.m_Backends,
1998 isSupported,
1999 inputInfo,
2000 outputInfo,
2001 desc);
2002 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +01002003 {
2004 return false;
2005 }
2006
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002007 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002008 assert(layer != nullptr);
2009 input.Connect(layer->GetInputSlot(0));
2010
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002011 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +01002012}
2013
Francis Murtagh074c25a2019-07-22 16:40:57 +01002014bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
2015{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01002016 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
2017
Francis Murtagh074c25a2019-07-22 16:40:57 +01002018 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2019 if (!input.IsValid())
2020 {
2021 return Fail("%s: Operation has invalid inputs", __func__);
2022 }
2023
2024 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2025 if (!outputOperand)
2026 {
2027 return Fail("%s: Operation has no outputs", __func__);
2028 }
2029
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002030 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01002031 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01002032 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002033 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002034 }
2035
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002036 SoftmaxDescriptor desc;
Francis Murtagh074c25a2019-07-22 16:40:57 +01002037 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
2038 {
2039 return Fail("%s: Operation has invalid inputs", __func__);
2040 }
2041
2042 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
2043 2,
2044 HalPolicy::OperandType::INT32,
2045 desc.m_Axis,
2046 model,
2047 data))
2048 {
2049 return Fail("%s: Operation has invalid inputs", __func__);
2050 }
2051
Narumol Prangnawarat52dc5272019-08-06 17:34:26 +01002052 if (input.GetTensorInfo().GetNumDimensions() > 2 ||
2053 !(desc.m_Axis == 1 ||
2054 (desc.m_Axis < 0 && static_cast<int>(input.GetTensorInfo().GetNumDimensions()) + desc.m_Axis == 1)))
2055 {
2056 return Fail("%s: Unsupported input greater than 2D or axis != 1", __func__);
2057 }
2058
Francis Murtagh074c25a2019-07-22 16:40:57 +01002059 bool isSupported = false;
2060 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2061 IsSoftmaxSupported,
2062 data.m_Backends,
2063 isSupported,
2064 input.GetTensorInfo(),
2065 outputInfo,
2066 desc);
2067 if (!isSupported)
2068 {
2069 return false;
2070 }
2071
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002072 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002073 assert(layer != nullptr);
2074 input.Connect(layer->GetInputSlot(0));
2075
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002076 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01002077}
2078
Mike Kelly0a879362019-07-29 16:56:31 +01002079bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2080{
2081 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
2082 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
2083}
2084
Sadik Armagan61113162019-07-25 09:09:40 +01002085bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
2086{
2087 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
2088 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
2089}
2090
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002091bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
2092{
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002093 ALOGV("hal_1_2::HalPolicy::ConvertLstm()");
2094
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002095 // Inputs:
2096 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2097 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2098 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2099 if (!input.IsValid())
2100 {
2101 return Fail("%s: Could not read input 0: input", __func__);
2102 }
2103 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2104 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
2105 if (!outputStateIn.IsValid())
2106 {
2107 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2108 }
2109 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2110 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
2111 if (!cellStateIn.IsValid())
2112 {
2113 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2114 }
2115
2116 // Get the mandatory input tensors:
2117 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2118 // [num_units, input_size].
2119 const ConstTensorPin inputToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002120 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 2));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002121 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2122 // [num_units, input_size].
2123 const ConstTensorPin inputToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002124 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 3));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002125 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2126 // [num_units, input_size].
2127 const ConstTensorPin inputToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002128 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 4));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002129 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2130 // [num_units, output_size].
2131 const ConstTensorPin recurrentToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002132 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 6));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002133 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2134 // [num_units, output_size].
2135 const ConstTensorPin recurrentToCellWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002136 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 7));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002137 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2138 // [num_units, output_size].
2139 const ConstTensorPin recurrentToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002140 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 8));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002141 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2142 const ConstTensorPin forgetGateBiasPin =
2143 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
2144 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2145 const ConstTensorPin cellBiasPin =
2146 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
2147 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2148 const ConstTensorPin outputGateBiasPin =
2149 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
2150
2151 if (!inputToForgetWeightsPin.IsValid() ||
2152 !inputToCellWeightsPin.IsValid() ||
2153 !inputToOutputWeightsPin.IsValid() ||
2154 !recurrentToForgetWeightsPin.IsValid() ||
2155 !recurrentToCellWeightsPin.IsValid() ||
2156 !recurrentToOutputWeightsPin.IsValid() ||
2157 !forgetGateBiasPin.IsValid() ||
2158 !cellBiasPin.IsValid() ||
2159 !outputGateBiasPin.IsValid())
2160 {
2161 return Fail("%s: Operation has invalid tensor inputs", __func__);
2162 }
2163
2164 // Get the optional input tensors:
2165 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2166 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2167 const ConstTensorPin inputToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002168 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 1, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002169 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2170 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2171 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2172 const ConstTensorPin recurrentToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002173 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 5, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002174 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2175 const ConstTensorPin cellToInputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002176 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 9, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002177 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2178 const ConstTensorPin cellToForgetWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002179 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 10, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002180 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2181 const ConstTensorPin cellToOutputWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002182 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 11, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002183 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2184 const ConstTensorPin inputGateBiasPin =
2185 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2186 12,
2187 model,
2188 data,
2189 g_DontPermute,
2190 nullptr,
2191 true);
2192
2193 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2194 // [output_size, num_units].
2195 const ConstTensorPin projectionWeightsPin =
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002196 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 16, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002197 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2198 const ConstTensorPin projectionBiasPin =
2199 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2200 17,
2201 model,
2202 data,
2203 g_DontPermute,
2204 nullptr,
2205 true);
2206
2207 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2208 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2209 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2210 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2211 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2212 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2213 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2214 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2215 {
2216 return Fail("%s: Operation has invalid tensor inputs", __func__);
2217 }
2218
2219 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2220 // 20: The activation function: A value indicating the activation function:
2221 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2222 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2223 // If set to 0.0 then clipping is disabled.
2224 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2225 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
2226 ActivationFn activation;
2227 float cellClip;
2228 float projClip;
2229 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
2230 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
2231 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
2232 {
2233 return Fail("%s: Operation has invalid scalar inputs", __func__);
2234 }
2235
2236 // Get the normalization tensors
2237 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2238 // Used to rescale normalized inputs to activation at input gate.
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002239 const ConstTensorPin inputLayerNormWeightsPin
2240 (DequantizeAndMakeConstTensorPin<hal_1_2::HalPolicy>(operation, model, data, 23, true));
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002241
2242 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2243 // Used to rescale normalized inputs to activation at forget gate.
2244 const ConstTensorPin forgetLayerNormWeightsPin =
2245 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2246 24,
2247 model,
2248 data,
2249 g_DontPermute,
2250 nullptr,
2251 true);
2252
2253 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2254 // Used to rescale normalized inputs to activation at cell gate.
2255 const ConstTensorPin cellLayerNormWeightsPin =
2256 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2257 25,
2258 model,
2259 data,
2260 g_DontPermute,
2261 nullptr,
2262 true);
2263
2264 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2265 // Used to rescale normalized inputs to activation at output gate.
2266 const ConstTensorPin outputLayerNormWeightsPin =
2267 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
2268 26,
2269 model,
2270 data,
2271 g_DontPermute,
2272 nullptr,
2273 true);
2274
2275 // Outputs:
2276 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2277 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2278 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2279 if (!scratchBuffer)
2280 {
2281 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2282 }
2283 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2284 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2285 if (!outputStateOut)
2286 {
2287 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2288 }
2289 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2290 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
2291 if (!cellStateOut)
2292 {
2293 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2294 }
2295 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2296 // effectively the same as the current “output state (out)” value.
2297 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
2298 if (!output)
2299 {
2300 return Fail("%s: Could not read output 3: output", __func__);
2301 }
2302
2303 // set the params structure for the AddLstmLayer call
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002304 LstmInputParams params;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002305 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2306 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2307 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2308 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2309 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2310 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2311 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2312 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2313 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2314 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2315 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2316 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2317 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2318 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2319 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2320 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2321 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2322 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2323 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2324 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2325 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2326
2327 // set the layer descriptor
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002328 LstmDescriptor desc;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002329 desc.m_ActivationFunc = activation;
2330 desc.m_ClippingThresCell = cellClip;
2331 desc.m_ClippingThresProj = projClip;
2332 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2333 params.m_RecurrentToInputWeights == nullptr ||
2334 params.m_InputGateBias == nullptr);
2335 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2336 params.m_CellToOutputWeights != nullptr);
2337 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2338 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2339 params.m_ForgetLayerNormWeights != nullptr ||
2340 params.m_CellLayerNormWeights != nullptr ||
2341 params.m_OutputLayerNormWeights != nullptr);
2342
2343 // validate the optional input groups
2344 if (desc.m_CifgEnabled &&
2345 (params.m_InputToInputWeights != nullptr ||
2346 params.m_RecurrentToInputWeights != nullptr ||
2347 params.m_InputGateBias != nullptr))
2348 {
2349 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2350 " and input gate bias must be provided", __func__);
2351 }
2352
2353 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2354 {
2355 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2356 }
2357
2358 if (desc.m_PeepholeEnabled &&
2359 (params.m_CellToForgetWeights == nullptr ||
2360 params.m_CellToOutputWeights == nullptr ||
2361 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2362 {
2363 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2364 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2365 }
2366
2367 if (desc.m_LayerNormEnabled &&
2368 (params.m_ForgetLayerNormWeights == nullptr ||
2369 params.m_CellLayerNormWeights == nullptr ||
2370 params.m_OutputLayerNormWeights == nullptr ||
2371 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2372 {
2373 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2374 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2375 }
2376
2377 // Check if the layer is supported
2378 // Inputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002379 const TensorInfo& inputInfo = input.GetTensorInfo();
2380 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2381 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002382
2383 // Outputs
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002384 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2385 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2386 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2387 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002388
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002389 if (IsDynamicTensor(scratchBufferInfo) ||
2390 IsDynamicTensor(outputStateOutInfo) ||
2391 IsDynamicTensor(cellStateOutInfo) ||
2392 IsDynamicTensor(outputInfo))
2393 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002394 return Fail("%s: Dynamic output tensors are not supported %d %d %d %d", __func__,
2395 IsDynamicTensor(scratchBufferInfo), IsDynamicTensor(outputStateOutInfo),
2396 IsDynamicTensor(cellStateOutInfo), IsDynamicTensor(outputInfo));
Ferran Balaguera4a629a2019-07-30 10:16:13 +01002397 }
2398
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002399 // Basic parameters
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002400 LstmInputParamsInfo paramsInfo;
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002401 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2402 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2403 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2404 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2405 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2406 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2407 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2408 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2409 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2410
2411 // Optional parameters
2412 if(!desc.m_CifgEnabled)
2413 {
2414 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2415 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2416 if (params.m_CellToInputWeights != nullptr)
2417 {
2418 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2419 }
2420 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2421 }
2422
2423 if(desc.m_ProjectionEnabled)
2424 {
2425 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2426 if (params.m_ProjectionBias != nullptr)
2427 {
2428 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2429 }
2430 }
2431
2432 if(desc.m_PeepholeEnabled)
2433 {
2434 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2435 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2436 }
2437
2438 if (desc.m_LayerNormEnabled)
2439 {
2440 if(!desc.m_CifgEnabled)
2441 {
2442 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2443 }
2444 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2445 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2446 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2447 }
2448
2449 bool isSupported = false;
2450 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2451 IsLstmSupported,
2452 data.m_Backends,
2453 isSupported,
2454 inputInfo,
2455 outputStateInInfo,
2456 cellStateInInfo,
2457 scratchBufferInfo,
2458 outputStateOutInfo,
2459 cellStateOutInfo,
2460 outputInfo,
2461 desc,
2462 paramsInfo);
2463 if (!isSupported)
2464 {
2465 return false;
2466 }
2467
2468 // Add the layer
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002469 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01002470
2471 input.Connect(layer->GetInputSlot(0));
2472 outputStateIn.Connect(layer->GetInputSlot(1));
2473 cellStateIn.Connect(layer->GetInputSlot(2));
2474
2475 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
2476 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
2477 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
2478 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
2479}
2480
Sadik Armagan701d9a02019-09-04 15:16:18 +01002481bool HalPolicy::ConvertSqrt(const Operation& operation, const Model& model, ConversionData& data)
2482{
2483 ALOGV("hal_1_2::HalPolicy::ConvertSqrt()");
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002484 ActivationDescriptor desc;
2485 desc.m_Function = ActivationFunction::Sqrt;
Sadik Armagan701d9a02019-09-04 15:16:18 +01002486
2487 return ::ConvertToActivation<hal_1_2::HalPolicy>(operation, __func__, desc, model, data);
2488}
2489
Mike Kelly46272802019-08-14 17:00:48 +01002490bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2491{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002492 ALOGV("hal_1_2::HalPolicy::ConvertSqueeze()");
Mike Kelly46272802019-08-14 17:00:48 +01002493 return ::ConvertSqueeze<hal_1_2::HalPolicy>(operation, model, data);
2494}
2495
2496bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2497{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002498 ALOGV("hal_1_2::HalPolicy::ConvertStridedSlice()");
Mike Kelly46272802019-08-14 17:00:48 +01002499 return ::ConvertStridedSlice<hal_1_2::HalPolicy>(operation, model, data);
2500}
2501
2502bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
2503{
Sadik Armagan701d9a02019-09-04 15:16:18 +01002504 ALOGV("hal_1_2::HalPolicy::ConvertTranspose()");
Mike Kelly46272802019-08-14 17:00:48 +01002505 return ::ConvertTranspose<hal_1_2::HalPolicy>(operation, model, data);
2506}
2507
Aron Virginas-Tar8b991682019-07-31 12:54:59 +01002508bool HalPolicy::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
David Monahan613b49c2019-06-27 11:37:47 +01002509{
2510 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
2511
2512 if (!input.IsValid())
2513 {
2514 return Fail("%s: Operation has invalid inputs", __func__);
2515 }
2516
2517 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
2518
2519 if (!output)
2520 {
2521 return Fail("%s: Could not read output 0", __func__);
2522 }
2523
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002524 const TensorInfo& inputInfo = input.GetTensorInfo();
2525 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
David Monahan613b49c2019-06-27 11:37:47 +01002526 if (IsDynamicTensor(outputInfo))
2527 {
2528 return Fail("%s: Dynamic output tensors are not supported", __func__);
2529 }
2530
2531 // ArmNN does not currently support non-fixed weights or bias
2532 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2533 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
2534
2535 if (weightsOperand == nullptr)
2536 {
2537 return Fail("%s: Operand is invalid", __func__);
2538 }
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002539 TransposeConvolution2dDescriptor desc;
2540 desc.m_DataLayout = DataLayout::NHWC;
David Monahan613b49c2019-06-27 11:37:47 +01002541
2542 // Determine whether padding is implicit or explicit
2543 bool implicitPadding = operation.inputs.size() == 9;
2544
2545 if (implicitPadding )
2546 {
2547 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
2548 }
2549 else
2550 {
2551 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
2552 }
2553
2554 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2555 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2556 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2557
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002558 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
David Monahan613b49c2019-06-27 11:37:47 +01002559
2560 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2561 // We have to permute it to OIHW if the data layout is NCHW.
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002562 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
David Monahan613b49c2019-06-27 11:37:47 +01002563 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
2564 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
2565
2566 // Bias is a 1D tensor
2567 const ConstTensorPin biasPin =
2568 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
2569
2570 if (!weightsPin.IsValid())
2571 {
2572 return Fail("%s: Operation has invalid weights", __func__);
2573 }
2574
2575 if (!biasPin.IsValid())
2576 {
2577 return Fail("%s: Operation has invalid biases", __func__);
2578 }
2579
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002580 ConstTensor weights = weightsPin.GetConstTensor();
2581 ConstTensor bias = biasPin.GetConstTensor();
David Monahan613b49c2019-06-27 11:37:47 +01002582 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2583
2584 ActivationFn activation;
2585
2586 if (implicitPadding)
2587 {
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002588 int32_t strideX{0};
2589 int32_t strideY{0};
2590 int32_t padLeft{0};
2591 int32_t padRight{0};
2592 int32_t padTop{0};
2593 int32_t padBottom{0};
2594
David Monahan613b49c2019-06-27 11:37:47 +01002595 android::nn::PaddingScheme paddingScheme;
2596 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002597 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, strideX, model, data) ||
2598 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, strideY, model, data) ||
David Monahan613b49c2019-06-27 11:37:47 +01002599 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
2600 {
2601 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2602 }
2603
2604 const uint32_t kernelX = weights.GetShape()[widthIndex];
2605 const uint32_t kernelY = weights.GetShape()[heightIndex];
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002606 const uint32_t outputX = outputInfo.GetShape()[widthIndex];
2607 const uint32_t outputY = outputInfo.GetShape()[heightIndex];
David Monahan613b49c2019-06-27 11:37:47 +01002608
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002609 CalcPaddingTransposeConv(outputX, kernelX, desc.m_StrideX, padLeft, padRight, paddingScheme);
2610 CalcPaddingTransposeConv(outputY, kernelY, desc.m_StrideY, padTop, padBottom, paddingScheme);
2611
2612 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
2613 // but Arm NN only supports values >= 0
2614 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
2615 {
2616 return Fail("%s: Negative padding values are not supported", __func__);
2617 }
2618
Sadik Armagan3e3003e2019-08-13 12:54:34 +01002619 desc.m_StrideX = boost::numeric_cast<uint32_t>(strideX);
2620 desc.m_StrideY = boost::numeric_cast<uint32_t>(strideY);
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +01002621 desc.m_PadLeft = boost::numeric_cast<uint32_t>(padLeft);
2622 desc.m_PadRight = boost::numeric_cast<uint32_t>(padRight);
2623 desc.m_PadTop = boost::numeric_cast<uint32_t>(padTop);
2624 desc.m_PadBottom = boost::numeric_cast<uint32_t>(padBottom);
David Monahan613b49c2019-06-27 11:37:47 +01002625 }
2626 else if (operation.inputs.size() == 11)
2627 {
2628 // explicit padding
2629 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
2630 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
2631 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
2632 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
2633 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
2634 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
2635 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
2636 {
2637 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
2638 }
2639 }
2640 else
2641 {
2642 return Fail("%s: Unsupported number of operation inputs", __func__);
2643 }
2644
2645 desc.m_BiasEnabled = true;
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002646 Optional<TensorInfo> biases(bias.GetInfo());
David Monahan613b49c2019-06-27 11:37:47 +01002647
2648 bool isSupported = false;
2649 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2650 IsTransposeConvolution2dSupported,
2651 data.m_Backends,
2652 isSupported,
2653 inputInfo,
2654 outputInfo,
2655 desc,
2656 weights.GetInfo(),
2657 biases);
2658 if (!isSupported)
2659 {
2660 return false;
2661 }
2662
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002663 IConnectableLayer* startLayer =
2664 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
David Monahan613b49c2019-06-27 11:37:47 +01002665 if (!startLayer)
2666 {
2667 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
2668 }
2669
Teresa Charlin8f6429d2019-10-01 13:10:15 +01002670 IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
David Monahan613b49c2019-06-27 11:37:47 +01002671 if (!endLayer)
2672 {
2673 return Fail("%s: ProcessActivation failed", __func__);
2674 }
2675
2676 input.Connect(startLayer->GetInputSlot(0));
2677
2678 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
2679}
2680
Mike Kellyb5fdf382019-06-11 16:35:25 +01002681} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01002682} // namespace armnn_driver