blob: 4372c166ca20adbdb1926aa6e4f5ddb9b50ea250 [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
Mike Kellyb5fdf382019-06-11 16:35:25 +010010#include "../1.0/HalPolicy.hpp"
11#include "../1.1/HalPolicy.hpp"
12
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010013#include <DataLayoutIndexed.hpp>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +010014#include <Half.hpp>
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +010015
16#include <cmath>
17
Mike Kellyb5fdf382019-06-11 16:35:25 +010018namespace armnn_driver
19{
20namespace hal_1_2
21{
22
23bool HandledByV1_0(V1_2::OperationType operationType)
24{
25 switch (static_cast<V1_0::OperationType>(operationType))
26 {
27 case V1_0::OperationType::ADD:
Mike Kellyb5fdf382019-06-11 16:35:25 +010028 case V1_0::OperationType::CONCATENATION:
29 case V1_0::OperationType::DEPTH_TO_SPACE:
30 case V1_0::OperationType::DEQUANTIZE:
31 case V1_0::OperationType::EMBEDDING_LOOKUP:
32 case V1_0::OperationType::FLOOR:
33 case V1_0::OperationType::FULLY_CONNECTED:
34 case V1_0::OperationType::HASHTABLE_LOOKUP:
35 case V1_0::OperationType::L2_NORMALIZATION:
Mike Kellyb5fdf382019-06-11 16:35:25 +010036 case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
37 case V1_0::OperationType::LOGISTIC:
38 case V1_0::OperationType::LSH_PROJECTION:
Mike Kellyb5fdf382019-06-11 16:35:25 +010039 case V1_0::OperationType::MUL:
Mike Kellyb5fdf382019-06-11 16:35:25 +010040 case V1_0::OperationType::RESHAPE:
Mike Kellyb5fdf382019-06-11 16:35:25 +010041 case V1_0::OperationType::RNN:
Mike Kellyb5fdf382019-06-11 16:35:25 +010042 case V1_0::OperationType::SVDF:
Mike Kellyb5fdf382019-06-11 16:35:25 +010043 case V1_0::OperationType::OEM_OPERATION:
44 return true;
45 default:
46 return false;
47 }
48}
49
50bool HandledByV1_1(V1_2::OperationType operationType)
51{
52 if (HandledByV1_0(operationType))
53 {
54 return true;
55 }
56 switch (static_cast<V1_1::OperationType>(operationType))
57 {
Mike Kellyb5fdf382019-06-11 16:35:25 +010058 case V1_1::OperationType::DIV:
59 case V1_1::OperationType::MEAN:
Mike Kellyb5fdf382019-06-11 16:35:25 +010060 case V1_1::OperationType::SPACE_TO_BATCH_ND:
61 case V1_1::OperationType::SQUEEZE:
62 case V1_1::OperationType::STRIDED_SLICE:
Mike Kellyb5fdf382019-06-11 16:35:25 +010063 case V1_1::OperationType::TRANSPOSE:
64 return true;
65 default:
66 return false;
67 }
68}
69
70bool HandledByV1_0(const V1_2::Operation& operation)
71{
72 return HandledByV1_0(operation.type);
73}
74
75bool HandledByV1_1(const V1_2::Operation& operation)
76{
77 return HandledByV1_1(operation.type);
78}
79
80V1_0::OperationType CastToV1_0(V1_2::OperationType type)
81{
82 return static_cast<V1_0::OperationType>(type);
83}
84
85V1_1::OperationType CastToV1_1(V1_2::OperationType type)
86{
87 return static_cast<V1_1::OperationType>(type);
88}
89
90V1_0::Operation ConvertToV1_0(const V1_2::Operation& operation)
91{
92 V1_0::Operation op;
93 op.type = CastToV1_0(operation.type);
94 op.inputs = operation.inputs;
95 op.outputs = operation.outputs;
96 return op;
97}
98
99V1_1::Operation ConvertToV1_1(const V1_2::Operation& operation)
100{
101 V1_1::Operation op;
102 op.type = CastToV1_1(operation.type);
103 op.inputs = operation.inputs;
104 op.outputs = operation.outputs;
105 return op;
106}
107
108bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
109{
110 if (HandledByV1_0(operation) && compliantWithV1_0(model))
111 {
112 hal_1_0::HalPolicy::Operation v10Operation = ConvertToV1_0(operation);
113 hal_1_0::HalPolicy::Model v10Model = convertToV1_0(model);
114
115 return hal_1_0::HalPolicy::ConvertOperation(v10Operation, v10Model, data);
116 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100117
118 if (HandledByV1_1(operation) && compliantWithV1_1(model))
Mike Kellyb5fdf382019-06-11 16:35:25 +0100119 {
120 hal_1_1::HalPolicy::Operation v11Operation = ConvertToV1_1(operation);
121 hal_1_1::HalPolicy::Model v11Model = convertToV1_1(model);
122
123 return hal_1_1::HalPolicy::ConvertOperation(v11Operation, v11Model, data);
124 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100125
Mike Kellyb5fdf382019-06-11 16:35:25 +0100126 switch (operation.type)
127 {
Sadik Armagan15d63e22019-07-26 16:59:35 +0100128 case V1_2::OperationType::AVERAGE_POOL_2D:
129 return ConvertAveragePool2d(operation, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +0100130 case V1_2::OperationType::BATCH_TO_SPACE_ND:
131 return ConvertBatchToSpaceNd(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100132 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100133 return ConvertConv2d(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100134 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100135 return ConvertDepthwiseConv2d(operation, model, data);
Sadik Armagan15d63e22019-07-26 16:59:35 +0100136 case V1_2::OperationType::L2_POOL_2D:
137 return ConvertL2Pool2d(operation, model, data);
138 case V1_2::OperationType::MAX_POOL_2D:
139 return ConvertMaxPool2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100140 case V1_2::OperationType::MAXIMUM:
141 return ConvertMaximum(operation, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100142 case V1_2::OperationType::MINIMUM:
143 return ConvertMinimum(operation, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +0100144 case V1_2::OperationType::PAD:
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100145 return ConvertPad(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100146 case V1_2::OperationType::PAD_V2:
147 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100148 case V1_2::OperationType::PRELU:
149 return ConvertPrelu(operation, model, data);
Sadik Armagan5a476a82019-07-30 09:43:18 +0100150 case V1_2::OperationType::QUANTIZE:
151 return ConvertQuantize(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100152 case V1_2::OperationType::RELU:
153 return ConvertReLu(operation, model, data);
154 case V1_2::OperationType::RELU1:
155 return ConvertReLu1(operation, model, data);
156 case V1_2::OperationType::RELU6:
157 return ConvertReLu6(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100158 case V1_2::OperationType::RESIZE_BILINEAR:
159 return ConvertResize(operation, model, data, armnn::ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100160 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100161 return ConvertResize(operation, model, data, armnn::ResizeMethod::NearestNeighbor);
David Monahan613b49c2019-06-27 11:37:47 +0100162 case V1_2::OperationType::TRANSPOSE_CONV_2D:
163 return ConvertTransposeConvolution2d(operation, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +0100164 case V1_2::OperationType::SOFTMAX:
165 return ConvertSoftmax(operation, model, data);
Aron Virginas-Tarad1ab532019-07-25 11:24:42 +0100166 case V1_2::OperationType::SPACE_TO_DEPTH:
167 return ConvertSpaceToDepth(operation, model, data);
Mike Kelly0a879362019-07-29 16:56:31 +0100168 case V1_2::OperationType::SUB:
169 return ConvertSub(operation, model, data);
Sadik Armagan61113162019-07-25 09:09:40 +0100170 case V1_2::OperationType::TANH:
171 return ConvertTanH(operation, model, data);
Ferran Balaguerb2397fd2019-07-25 12:12:39 +0100172 case V1_2::OperationType::LSTM:
173 return ConvertLstm(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100174 default:
175 return Fail("%s: Operation type %s not supported in ArmnnDriver",
176 __func__, toString(operation.type).c_str());
177 }
178}
179
Sadik Armagan15d63e22019-07-26 16:59:35 +0100180bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
181{
182 ALOGV("hal_1_2::HalPolicy::ConvertAveragePool2d()");
183 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Average, model, data);
184}
185
Finn Williams23b87b32019-07-30 11:44:05 +0100186bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data)
187{
188 ALOGV("hal_1_2::HalPolicy::ConvertBatchToSpaceNd()");
189 return ::ConvertBatchToSpaceNd<hal_1_2::HalPolicy>(operation, model, data);
190}
191
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100192bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
193{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100194 ALOGV("hal_1_2::HalPolicy::ConvertConv2d()");
195
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100196 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
197 if (!input.IsValid())
198 {
199 return Fail("%s: Operation has invalid inputs", __func__);
200 }
201
202 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
203 if (!output)
204 {
205 return Fail("%s: Could not read output 0", __func__);
206 }
207
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100208 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
209 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
210
211 if (IsDynamicTensor(outputInfo))
212 {
213 return Fail("%s: Dynamic output tensors are not supported", __func__);
214 }
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100215
Mike Kellye1d60bb2019-07-11 11:44:52 +0100216 armnn::Convolution2dDescriptor desc;
217 desc.m_DataLayout = armnn::DataLayout::NHWC;
218
219 // Determine whether padding is implicit or explicit
220 bool implicitPadding = operation.inputs.size() == 7 ||
221 (operation.inputs.size() >= 8 &&
222 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
223
224 if (implicitPadding)
225 {
226 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
227 }
228 else if (operation.inputs.size() >= 10)
229 {
230 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
231 }
232
233 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
234
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100235 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100236 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
237 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
238 // the DataLayout is NCHW
239 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
240 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
241 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100242 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100243 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100244
245 if (!weightsPin.IsValid())
246 {
247 return Fail("%s: Operation has invalid weights", __func__);
248 }
249
250 if (!biasPin.IsValid())
251 {
252 return Fail("%s: Operation has invalid biases", __func__);
253 }
254
255 armnn::ConstTensor weights = weightsPin.GetConstTensor();
256 armnn::ConstTensor bias = biasPin.GetConstTensor();
257 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
258
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100259 ActivationFn activation;
260
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100261 if (implicitPadding)
262 {
263 android::nn::PaddingScheme paddingScheme;
264 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
265 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
266 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
267 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
268 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
269 {
270 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
271 }
272
Mike Kellye1d60bb2019-07-11 11:44:52 +0100273 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
274 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
275 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
276 const uint32_t kernelX = weights.GetShape()[widthIndex];
277 const uint32_t kernelY = weights.GetShape()[heightIndex];
278 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
279 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100280
Mike Kelly86b36d42019-07-12 16:39:33 +0100281 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
282 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100283
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100284 }
285 else if (operation.inputs.size() >= 10)
286 {
287 // explicit padding
288 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
289 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
290 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
291 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
292 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
293 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
294 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
295 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
296 {
297 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
298 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100299 }
300 else
301 {
302 return Fail("%s: Unsupported number of operation inputs", __func__);
303 }
304
305 desc.m_BiasEnabled = true;
306 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
307
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100308 bool isSupported = false;
309 FORWARD_LAYER_SUPPORT_FUNC(__func__,
310 IsConvolution2dSupported,
311 data.m_Backends,
312 isSupported,
313 inputInfo,
314 outputInfo,
315 desc,
316 weights.GetInfo(),
317 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100318
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100319 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100320 {
321 return false;
322 }
323
324 armnn::IConnectableLayer* startLayer =
325 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
326
327 if (!startLayer)
328 {
329 return Fail("%s: AddConvolution2dLayer failed", __func__);
330 }
331
332 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
333
334 if (!endLayer)
335 {
336 return Fail("%s: ProcessActivation failed", __func__);
337 }
338
339 input.Connect(startLayer->GetInputSlot(0));
340
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100341 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100342}
343
344bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
345{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100346 ALOGV("hal_1_2::HalPolicy::ConvertDepthwiseConv2d()");
347
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100348 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
349
350 if (!input.IsValid())
351 {
352 return Fail("%s: Operation has invalid inputs", __func__);
353 }
354
355 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
356
357 if (!output)
358 {
359 return Fail("%s: Could not read output 0", __func__);
360 }
361
362 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100363 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
364
365 if (IsDynamicTensor(outputInfo))
366 {
367 return Fail("%s: Dynamic output tensors are not supported", __func__);
368 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100369
370 // ArmNN does not currently support non-fixed weights or bias
371 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
372 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
373
374 if (weightsOperand == nullptr)
375 {
376 return Fail("%s: Operand is invalid", __func__);
377 }
378 armnn::DepthwiseConvolution2dDescriptor desc;
379 desc.m_DataLayout = armnn::DataLayout::NHWC;
380
381 // Determine whether padding is implicit or explicit
382 bool implicitPadding = operation.inputs.size() == 8 ||
383 (operation.inputs.size() >= 9 &&
384 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
385
386 // Look ahead to find the optional DataLayout, if present
387 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
388 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
389
390 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
391 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
392 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
393 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
394
395 // Reinterpret weight data as [ H, W, I, M ]
396 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
397 weightsOperand->dimensions[2],
398 inputInfo.GetShape()[channelsIndex],
399 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
400
401 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
402 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
403
404 const ConstTensorPin weightsPin =
405 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
406 1,
407 model,
408 data,
409 HWIMToMIHW,
410 &weightsShape);
411
412 // Bias is a 1D tensor
413 const ConstTensorPin biasPin =
414 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
415
416 if (!weightsPin.IsValid())
417 {
418 return Fail("%s: Operation has invalid weights", __func__);
419 }
420
421 if (!biasPin.IsValid())
422 {
423 return Fail("%s: Operation has invalid biases", __func__);
424 }
425
426 armnn::ConstTensor weights = weightsPin.GetConstTensor();
427 armnn::ConstTensor bias = biasPin.GetConstTensor();
428 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
429
430 ActivationFn activation;
431
432 if (implicitPadding)
433 {
434 android::nn::PaddingScheme paddingScheme;
435 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
436 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
437 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
438 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
439 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
440 {
441 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
442 }
443
444 const uint32_t kernelX = weights.GetShape()[3];
445 const uint32_t kernelY = weights.GetShape()[2];
446 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
447 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
448
Mike Kelly86b36d42019-07-12 16:39:33 +0100449 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
450 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100451 }
452 else if (operation.inputs.size() >= 11)
453 {
454 // explicit padding
455 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
456 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
457 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
458 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
459 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
460 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
461 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
462 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
463 {
464 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
465 }
466 }
467 else
468 {
469 return Fail("%s: Unsupported number of operation inputs", __func__);
470 }
471
472 desc.m_BiasEnabled = true;
473 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
474
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100475 bool isSupported = false;
476 FORWARD_LAYER_SUPPORT_FUNC(__func__,
477 IsDepthwiseConvolutionSupported,
478 data.m_Backends,
479 isSupported,
480 inputInfo,
481 outputInfo,
482 desc,
483 weights.GetInfo(),
484 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100485
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100486 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100487 {
488 return false;
489 }
490
491 armnn::IConnectableLayer* startLayer =
492 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100493
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100494 if (!startLayer)
495 {
496 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
497 }
498
499 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
500 if (!endLayer)
501 {
502 return Fail("%s: ProcessActivation failed", __func__);
503 }
504
505 input.Connect(startLayer->GetInputSlot(0));
506
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100507 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100508}
509
Sadik Armagan15d63e22019-07-26 16:59:35 +0100510bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
511{
512 ALOGV("hal_1_2::HalPolicy::ConvertL2Pool2d()");
513 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
514}
515
516bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
517{
518 ALOGV("hal_1_2::HalPolicy::ConvertMaxPool2d()");
519 return ConvertPooling2d<hal_1_2::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
520}
521
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100522bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
523{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100524 ALOGV("hal_1_2::HalPolicy::ConvertMaximum()");
525
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100526 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
527 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
528
529 if (!input0.IsValid() || !input1.IsValid())
530 {
531 return Fail("%s: Operation has invalid inputs", __func__);
532 }
533
534 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
535 if (!outputOperand)
536 {
537 return Fail("%s: Could not read output", __func__);
538 }
539
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100540 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100541 if (IsDynamicTensor(outInfo))
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100542 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100543 return Fail("%s: Dynamic output tensors are not supported", __func__);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100544 }
545
Aron Virginas-Tard7593232019-07-16 13:17:06 +0100546 bool isSupported = false;
547 FORWARD_LAYER_SUPPORT_FUNC(__func__,
548 IsMaximumSupported,
549 data.m_Backends,
550 isSupported,
551 input0.GetTensorInfo(),
552 input1.GetTensorInfo(),
553 outInfo);
554
555 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100556 {
557 return false;
558 }
559
560 armnn::IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
561 assert(layer != nullptr);
562 BroadcastTensor(input0, input1, layer, *data.m_Network);
563
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100564 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100565}
566
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100567bool HalPolicy::ConvertMinimum(const Operation& operation, const Model& model, ConversionData& data)
568{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100569 ALOGV("hal_1_2::HalPolicy::ConvertMinimum()");
570
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100571 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
572 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
573
574 if (!input0.IsValid() || !input1.IsValid())
575 {
576 return Fail("%s: Operation has invalid inputs", __func__);
577 }
578
579 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
580 if (!output)
581 {
582 return Fail("%s: Could not read output 0", __func__);
583 }
584
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100585 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100586 if (IsDynamicTensor(outputInfo))
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100587 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100588 return Fail("%s: Dynamic output tensors are not supported", __func__);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100589 }
590
591 bool isSupported = false;
592 FORWARD_LAYER_SUPPORT_FUNC(__func__,
593 IsMinimumSupported,
594 data.m_Backends,
595 isSupported,
596 input0.GetTensorInfo(),
597 input1.GetTensorInfo(),
598 outputInfo);
599
600 if (!isSupported)
601 {
602 return false;
603 }
604
605 armnn::IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
606 assert(layer != nullptr);
607 BroadcastTensor(input0, input1, layer, *data.m_Network);
608
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100609 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100610}
611
Aron Virginas-Tarc921f6b2019-07-25 10:14:33 +0100612bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
613{
614 ALOGV("hal_1_2::HalPolicy::ConvertPad()");
615 return ::ConvertPad<hal_1_2::HalPolicy>(operation, model, data);
616}
617
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100618bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
619{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100620 ALOGV("hal_1_2::HalPolicy::ConvertPadV2()");
621
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100622 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
623 if (!input.IsValid())
624 {
625 return Fail("%s: Could not read input 0", __func__);
626 }
627
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100628 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
629 if (!output)
630 {
631 return Fail("%s: Could not read output", __func__);
632 }
633
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100634 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
635 unsigned int rank = inputInfo.GetNumDimensions();
636
637 armnn::PadDescriptor descriptor;
638 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
639 {
640 return Fail("%s: Could not convert paddings", __func__);
641 }
642
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100643 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100644 if (IsDynamicTensor(outputInfo))
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100645 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100646 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100647 }
648
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100649 // Determine type of padding value
650 OperandType operandType0;
651 OperandType operandType2;
652
653 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
654 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
655 {
656 return Fail("%s: Operation has invalid inputs", __func__);
657 }
658
659 // Read value to use for padding
660 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
661 {
662 armnn::Half f16PadValue;
663 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
664 {
665 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
666 }
667
668 descriptor.m_PadValue = f16PadValue;
669 }
670 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
671 {
672 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
673 {
674 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
675 }
676 }
677 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
678 {
Mike Kelly3c673942019-07-25 09:26:06 +0100679 int32_t intPadValue = 0;
680 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, intPadValue, model, data))
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100681 {
682 return Fail("%s: Could not read input 2 (INT32)", __func__);
683 }
Mike Kelly3c673942019-07-25 09:26:06 +0100684 descriptor.m_PadValue = intPadValue;
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100685 }
686 else
687 {
688 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
689 }
690
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100691 bool isSupported = false;
692 FORWARD_LAYER_SUPPORT_FUNC(__func__,
693 IsPadSupported,
694 data.m_Backends,
695 isSupported,
696 inputInfo,
697 outputInfo,
698 descriptor);
699 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100700 {
701 return false;
702 }
703
704 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
705 assert(layer != nullptr);
706 input.Connect(layer->GetInputSlot(0));
707 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
708
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100709 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100710}
711
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100712bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
713{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100714 ALOGV("hal_1_2::HalPolicy::ConvertPrelu()");
715
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100716 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
717 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
718
719 if (!input.IsValid() || !alpha.IsValid())
720 {
721 return Fail("%s: Operation has invalid inputs", __func__);
722 }
723
724 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
725
726 if (!output)
727 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100728 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100729 }
730
731 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
732 const armnn::TensorInfo& alphaInfo = alpha.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100733 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100734
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100735 if (IsDynamicTensor(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100736 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100737 return Fail("%s: Dynamic output tensors are not supported", __func__);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100738 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100739
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100740 bool isSupported = false;
741 FORWARD_LAYER_SUPPORT_FUNC(__func__,
742 IsPreluSupported,
743 data.m_Backends,
744 isSupported,
745 inputInfo,
746 alphaInfo,
747 outputInfo);
748 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100749 {
750 return false;
751 }
752
753 armnn::IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
754
755 if (!layer)
756 {
757 return Fail("%s: AddPreluLayer failed", __func__);
758 }
759
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100760 BroadcastTensor(input, alpha, layer, *data.m_Network);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100761
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100762 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100763}
764
Sadik Armagan5a476a82019-07-30 09:43:18 +0100765bool HalPolicy::ConvertQuantize(const Operation& operation, const Model& model, ConversionData& data)
766{
767 ALOGV("hal_1_2::HalPolicy::ConvertQuantize()");
768
769 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
770 if (!input.IsValid())
771 {
772 return Fail("%s: Operation has invalid input", __func__);
773 }
774
775 const Operand* const outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
776 if (!outputOperand)
777 {
778 return Fail("%s: Operation has invalid outputs", __func__);
779 }
780
781 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
782 if (IsDynamicTensor(outputInfo))
783 {
784 return Fail("%s: Dynamic output tensors are not supported", __func__);
785 }
786
787 bool isSupported = false;
788 FORWARD_LAYER_SUPPORT_FUNC(__func__,
789 IsQuantizeSupported,
790 data.m_Backends,
791 isSupported,
792 input.GetTensorInfo(),
793 outputInfo);
794 if (!isSupported)
795 {
796 return false;
797 }
798
799 armnn::IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
800 assert(layer != nullptr);
801 input.Connect(layer->GetInputSlot(0));
802
803 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
804}
805
Sadik Armagan61113162019-07-25 09:09:40 +0100806bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
807{
808 ALOGV("hal_1_2::HalPolicy::ConvertReLu()");
809 return ::ConvertReLu<hal_1_2::HalPolicy>(operation, model, data);
810}
811
812bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
813{
814 ALOGV("hal_1_2::HalPolicy::ConvertReLu1()");
815 return ::ConvertReLu1<hal_1_2::HalPolicy>(operation, model, data);
816}
817
818bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
819{
820 ALOGV("hal_1_2::HalPolicy::ConvertReLu6()");
821 return ::ConvertReLu6<hal_1_2::HalPolicy>(operation, model, data);
822}
823
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100824bool HalPolicy::ConvertResize(const Operation& operation,
825 const Model& model,
826 ConversionData& data,
827 armnn::ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100828{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100829 ALOGV("hal_1_2::HalPolicy::ConvertResize()");
830
831 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100832 if (!input.IsValid())
833 {
834 return Fail("%s: Could not read input 0", __func__);
835 }
836
837 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
838 if (!output)
839 {
840 return Fail("%s: Could not read output 0", __func__);
841 }
842
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100843 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
844 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
845
846 if (IsDynamicTensor(outputInfo))
847 {
848 return Fail("%s: Dynamic output tensors are not supported", __func__);
849 }
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100850
851 armnn::ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100852 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100853 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
854
855 OperandType operandType1;
856 OperandType operandType2;
857
858 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
859 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
860 {
861 return Fail("%s: Operation has invalid inputs", __func__);
862 }
863
864 if (operandType1 != operandType2)
865 {
866 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
867 }
868
869 if (operandType1 == OperandType::INT32)
870 {
871 // Case 1: resizing by shape
872 int32_t targetWidth = 0;
873 int32_t targetHeight = 0;
874
875 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
876 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
877 {
878 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
879 }
880
881 if (targetWidth < 0 || targetHeight < 0)
882 {
883 return Fail("%s: Operation has invalid inputs for resizing by shape. "
884 "Target width/height cannot be < 0", __func__);
885 }
886
887 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
Teresa Charlin9843c012019-07-19 12:18:35 +0100888 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100889 }
890 else if (operandType1 == OperandType::FLOAT32)
891 {
892 // Case 2: resizing by scale
893 float widthScale = 1.0f;
894 float heightScale = 1.0f;
895
896 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
897 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
898 {
899 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
900 }
901
902 const armnn::TensorShape& inputShape = inputInfo.GetShape();
903 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
904
905 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
906 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
907
908 descriptor.m_TargetWidth = std::floor(width * widthScale);
909 descriptor.m_TargetHeight = std::floor(height * heightScale);
910 }
911 else
912 {
913 // NOTE: FLOAT16 scales are not supported
914 return false;
915 }
916
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100917 bool isSupported = false;
918 FORWARD_LAYER_SUPPORT_FUNC(__func__,
919 IsResizeSupported,
920 data.m_Backends,
921 isSupported,
922 inputInfo,
923 outputInfo,
924 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +0100925
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100926 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100927 {
928 return false;
929 }
930
931 armnn::IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
932
933 assert(layer != nullptr);
934
935 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
936 input.Connect(layer->GetInputSlot(0));
937
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100938 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100939}
940
Keith Davisa6bc52f2019-06-26 09:39:49 +0100941bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
942{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100943 ALOGV("hal_1_2::HalPolicy::ConvertSpaceToDepth()");
Keith Davisa6bc52f2019-06-26 09:39:49 +0100944
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +0100945 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +0100946 if (!input.IsValid() )
947 {
948 return Fail("%s: Operation has invalid inputs", __func__);
949 }
950
951 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
952 unsigned int rank = inputInfo.GetNumDimensions();
Keith Davisa6bc52f2019-06-26 09:39:49 +0100953 if (rank != 4)
954 {
955 return Fail("%s: Only inputs with rank 4 are supported", __func__);
956 }
957
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100958 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
959 if (!output)
960 {
961 return Fail("%s: Could not read output 0", __func__);
962 }
963
964 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
965 if (IsDynamicTensor(outputInfo))
966 {
967 return Fail("%s: Dynamic output tensors are not supported", __func__);
968 }
969
Keith Davisa6bc52f2019-06-26 09:39:49 +0100970 armnn::SpaceToDepthDescriptor desc;
971
972 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
973
974 if (desc.m_BlockSize <= 1)
975 {
976 return Fail("%s: Block size must be at least 1 in all dimensions");
977 }
978
979 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
980
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100981 bool isSupported = false;
982 FORWARD_LAYER_SUPPORT_FUNC(__func__,
983 IsSpaceToDepthSupported,
984 data.m_Backends,
985 isSupported,
986 inputInfo,
987 outputInfo,
988 desc);
989 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +0100990 {
991 return false;
992 }
993
994 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
995 assert(layer != nullptr);
996 input.Connect(layer->GetInputSlot(0));
997
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +0100998 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +0100999}
1000
Francis Murtagh074c25a2019-07-22 16:40:57 +01001001bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1002{
Aron Virginas-Tar29404fb2019-07-24 13:55:31 +01001003 ALOGV("hal_1_2::HalPolicy::ConvertSoftmax()");
1004
Francis Murtagh074c25a2019-07-22 16:40:57 +01001005 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1006 if (!input.IsValid())
1007 {
1008 return Fail("%s: Operation has invalid inputs", __func__);
1009 }
1010
1011 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1012 if (!outputOperand)
1013 {
1014 return Fail("%s: Operation has no outputs", __func__);
1015 }
1016
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001017 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001018 if (IsDynamicTensor(outputInfo))
Francis Murtagh074c25a2019-07-22 16:40:57 +01001019 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001020 return Fail("%s: Dynamic output tensors are not supported", __func__);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001021 }
1022
1023 armnn::SoftmaxDescriptor desc;
1024 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, desc.m_Beta, model, data))
1025 {
1026 return Fail("%s: Operation has invalid inputs", __func__);
1027 }
1028
1029 if (operation.inputs.size() > 2 && !GetInputScalar<hal_1_2::HalPolicy>(operation,
1030 2,
1031 HalPolicy::OperandType::INT32,
1032 desc.m_Axis,
1033 model,
1034 data))
1035 {
1036 return Fail("%s: Operation has invalid inputs", __func__);
1037 }
1038
1039 bool isSupported = false;
1040 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1041 IsSoftmaxSupported,
1042 data.m_Backends,
1043 isSupported,
1044 input.GetTensorInfo(),
1045 outputInfo,
1046 desc);
1047 if (!isSupported)
1048 {
1049 return false;
1050 }
1051
1052 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1053 assert(layer != nullptr);
1054 input.Connect(layer->GetInputSlot(0));
1055
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001056 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
Francis Murtagh074c25a2019-07-22 16:40:57 +01001057}
1058
Mike Kelly0a879362019-07-29 16:56:31 +01001059bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
1060{
1061 ALOGV("hal_1_2::HalPolicy::ConvertSub()");
1062 return ::ConvertSub<hal_1_2::HalPolicy>(operation, model, data);
1063}
1064
Sadik Armagan61113162019-07-25 09:09:40 +01001065bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1066{
1067 ALOGV("hal_1_2::HalPolicy::ConvertTanH()");
1068 return ::ConvertTanH<hal_1_2::HalPolicy>(operation, model, data);
1069}
1070
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001071bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
1072{
1073 // Inputs:
1074 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
1075 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
1076 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1077 if (!input.IsValid())
1078 {
1079 return Fail("%s: Could not read input 0: input", __func__);
1080 }
1081 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1082 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 18, model, data);
1083 if (!outputStateIn.IsValid())
1084 {
1085 return Fail("%s: Could not read input 18: outputStateIn", __func__);
1086 }
1087 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1088 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 19, model, data);
1089 if (!cellStateIn.IsValid())
1090 {
1091 return Fail("%s: Could not read input 19: cellStateIn", __func__);
1092 }
1093
1094 // Get the mandatory input tensors:
1095 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1096 // [num_units, input_size].
1097 const ConstTensorPin inputToForgetWeightsPin =
1098 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1099 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1100 // [num_units, input_size].
1101 const ConstTensorPin inputToCellWeightsPin =
1102 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 3, model, data);
1103 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1104 // [num_units, input_size].
1105 const ConstTensorPin inputToOutputWeightsPin =
1106 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 4, model, data);
1107 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1108 // [num_units, output_size].
1109 const ConstTensorPin recurrentToForgetWeightsPin =
1110 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 6, model, data);
1111 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1112 // [num_units, output_size].
1113 const ConstTensorPin recurrentToCellWeightsPin =
1114 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 7, model, data);
1115 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1116 // [num_units, output_size].
1117 const ConstTensorPin recurrentToOutputWeightsPin =
1118 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 8, model, data);
1119 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1120 const ConstTensorPin forgetGateBiasPin =
1121 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 13, model, data);
1122 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1123 const ConstTensorPin cellBiasPin =
1124 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 14, model, data);
1125 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1126 const ConstTensorPin outputGateBiasPin =
1127 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 15, model, data);
1128
1129 if (!inputToForgetWeightsPin.IsValid() ||
1130 !inputToCellWeightsPin.IsValid() ||
1131 !inputToOutputWeightsPin.IsValid() ||
1132 !recurrentToForgetWeightsPin.IsValid() ||
1133 !recurrentToCellWeightsPin.IsValid() ||
1134 !recurrentToOutputWeightsPin.IsValid() ||
1135 !forgetGateBiasPin.IsValid() ||
1136 !cellBiasPin.IsValid() ||
1137 !outputGateBiasPin.IsValid())
1138 {
1139 return Fail("%s: Operation has invalid tensor inputs", __func__);
1140 }
1141
1142 // Get the optional input tensors:
1143 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1144 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
1145 const ConstTensorPin inputToInputWeightsPin =
1146 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1147 1,
1148 model,
1149 data,
1150 g_DontPermute,
1151 nullptr,
1152 true);
1153
1154 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1155 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
1156 // “num_units”), or the second dimension of the “projection_weights”, if defined.
1157 const ConstTensorPin recurrentToInputWeightsPin =
1158 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1159 5,
1160 model,
1161 data,
1162 g_DontPermute,
1163 nullptr,
1164 true);
1165
1166 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1167 const ConstTensorPin cellToInputWeightsPin =
1168 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1169 9,
1170 model,
1171 data,
1172 g_DontPermute,
1173 nullptr,
1174 true);
1175
1176 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1177 const ConstTensorPin cellToForgetWeightsPin =
1178 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1179 10,
1180 model,
1181 data,
1182 g_DontPermute,
1183 nullptr,
1184 true);
1185
1186 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1187 const ConstTensorPin cellToOutputWeightsPin =
1188 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1189 11,
1190 model,
1191 data,
1192 g_DontPermute,
1193 nullptr,
1194 true);
1195
1196 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
1197 const ConstTensorPin inputGateBiasPin =
1198 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1199 12,
1200 model,
1201 data,
1202 g_DontPermute,
1203 nullptr,
1204 true);
1205
1206 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
1207 // [output_size, num_units].
1208 const ConstTensorPin projectionWeightsPin =
1209 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1210 16,
1211 model,
1212 data,
1213 g_DontPermute,
1214 nullptr,
1215 true);
1216
1217 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
1218 const ConstTensorPin projectionBiasPin =
1219 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1220 17,
1221 model,
1222 data,
1223 g_DontPermute,
1224 nullptr,
1225 true);
1226
1227 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
1228 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
1229 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
1230 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
1231 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
1232 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
1233 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
1234 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
1235 {
1236 return Fail("%s: Operation has invalid tensor inputs", __func__);
1237 }
1238
1239 // Get the mandatory input scalars (actually 1-D tensors of size 1):
1240 // 20: The activation function: A value indicating the activation function:
1241 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
1242 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
1243 // If set to 0.0 then clipping is disabled.
1244 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
1245 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
1246 ActivationFn activation;
1247 float cellClip;
1248 float projClip;
1249 if (!GetInputActivationFunctionFromTensor<hal_1_2::HalPolicy>(operation, 20, activation, model, data) ||
1250 !GetInputScalar<hal_1_2::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
1251 !GetInputScalar<hal_1_2::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
1252 {
1253 return Fail("%s: Operation has invalid scalar inputs", __func__);
1254 }
1255
1256 // Get the normalization tensors
1257 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
1258 // Used to rescale normalized inputs to activation at input gate.
1259 const ConstTensorPin inputLayerNormWeightsPin =
1260 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1261 23,
1262 model,
1263 data,
1264 g_DontPermute,
1265 nullptr,
1266 true);
1267
1268 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
1269 // Used to rescale normalized inputs to activation at forget gate.
1270 const ConstTensorPin forgetLayerNormWeightsPin =
1271 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1272 24,
1273 model,
1274 data,
1275 g_DontPermute,
1276 nullptr,
1277 true);
1278
1279 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
1280 // Used to rescale normalized inputs to activation at cell gate.
1281 const ConstTensorPin cellLayerNormWeightsPin =
1282 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1283 25,
1284 model,
1285 data,
1286 g_DontPermute,
1287 nullptr,
1288 true);
1289
1290 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
1291 // Used to rescale normalized inputs to activation at output gate.
1292 const ConstTensorPin outputLayerNormWeightsPin =
1293 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
1294 26,
1295 model,
1296 data,
1297 g_DontPermute,
1298 nullptr,
1299 true);
1300
1301 // Outputs:
1302 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
1303 // with CIFG, or [batch_size, num_units * 3] without CIFG.
1304 const Operand* scratchBuffer = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1305 if (!scratchBuffer)
1306 {
1307 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
1308 }
1309 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
1310 const Operand* outputStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1311 if (!outputStateOut)
1312 {
1313 return Fail("%s: Could not read output 1: outputStateOut", __func__);
1314 }
1315 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
1316 const Operand* cellStateOut = GetOutputOperand<hal_1_2::HalPolicy>(operation, 2, model);
1317 if (!cellStateOut)
1318 {
1319 return Fail("%s: Could not read output 2: cellStateOut", __func__);
1320 }
1321 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
1322 // effectively the same as the current “output state (out)” value.
1323 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 3, model);
1324 if (!output)
1325 {
1326 return Fail("%s: Could not read output 3: output", __func__);
1327 }
1328
1329 // set the params structure for the AddLstmLayer call
1330 armnn::LstmInputParams params;
1331 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1332 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1333 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1334 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1335 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1336 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1337 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1338 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1339 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
1340 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
1341 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
1342 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1343 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1344 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1345 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1346 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
1347 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
1348 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
1349 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
1350 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
1351 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
1352
1353 // set the layer descriptor
1354 armnn::LstmDescriptor desc;
1355 desc.m_ActivationFunc = activation;
1356 desc.m_ClippingThresCell = cellClip;
1357 desc.m_ClippingThresProj = projClip;
1358 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
1359 params.m_RecurrentToInputWeights == nullptr ||
1360 params.m_InputGateBias == nullptr);
1361 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
1362 params.m_CellToOutputWeights != nullptr);
1363 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
1364 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
1365 params.m_ForgetLayerNormWeights != nullptr ||
1366 params.m_CellLayerNormWeights != nullptr ||
1367 params.m_OutputLayerNormWeights != nullptr);
1368
1369 // validate the optional input groups
1370 if (desc.m_CifgEnabled &&
1371 (params.m_InputToInputWeights != nullptr ||
1372 params.m_RecurrentToInputWeights != nullptr ||
1373 params.m_InputGateBias != nullptr))
1374 {
1375 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
1376 " and input gate bias must be provided", __func__);
1377 }
1378
1379 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
1380 {
1381 return Fail("%s: projection bias should not be provided without projection weights", __func__);
1382 }
1383
1384 if (desc.m_PeepholeEnabled &&
1385 (params.m_CellToForgetWeights == nullptr ||
1386 params.m_CellToOutputWeights == nullptr ||
1387 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
1388 {
1389 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
1390 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
1391 }
1392
1393 if (desc.m_LayerNormEnabled &&
1394 (params.m_ForgetLayerNormWeights == nullptr ||
1395 params.m_CellLayerNormWeights == nullptr ||
1396 params.m_OutputLayerNormWeights == nullptr ||
1397 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
1398 {
1399 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
1400 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
1401 }
1402
1403 // Check if the layer is supported
1404 // Inputs
1405 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1406 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
1407 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
1408
1409 // Outputs
1410 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
1411 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
1412 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1413 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1414
Ferran Balaguera4a629a2019-07-30 10:16:13 +01001415 if (IsDynamicTensor(scratchBufferInfo) ||
1416 IsDynamicTensor(outputStateOutInfo) ||
1417 IsDynamicTensor(cellStateOutInfo) ||
1418 IsDynamicTensor(outputInfo))
1419 {
1420 return Fail("%s: Dynamic output tensors are not supported", __func__);
1421 }
1422
Ferran Balaguerb2397fd2019-07-25 12:12:39 +01001423 // Basic parameters
1424 armnn::LstmInputParamsInfo paramsInfo;
1425 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1426 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1427 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1428 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1429 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1430 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1431 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1432 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1433 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1434
1435 // Optional parameters
1436 if(!desc.m_CifgEnabled)
1437 {
1438 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1439 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1440 if (params.m_CellToInputWeights != nullptr)
1441 {
1442 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
1443 }
1444 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1445 }
1446
1447 if(desc.m_ProjectionEnabled)
1448 {
1449 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
1450 if (params.m_ProjectionBias != nullptr)
1451 {
1452 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
1453 }
1454 }
1455
1456 if(desc.m_PeepholeEnabled)
1457 {
1458 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
1459 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
1460 }
1461
1462 if (desc.m_LayerNormEnabled)
1463 {
1464 if(!desc.m_CifgEnabled)
1465 {
1466 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
1467 }
1468 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
1469 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
1470 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
1471 }
1472
1473 bool isSupported = false;
1474 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1475 IsLstmSupported,
1476 data.m_Backends,
1477 isSupported,
1478 inputInfo,
1479 outputStateInInfo,
1480 cellStateInInfo,
1481 scratchBufferInfo,
1482 outputStateOutInfo,
1483 cellStateOutInfo,
1484 outputInfo,
1485 desc,
1486 paramsInfo);
1487 if (!isSupported)
1488 {
1489 return false;
1490 }
1491
1492 // Add the layer
1493 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
1494
1495 input.Connect(layer->GetInputSlot(0));
1496 outputStateIn.Connect(layer->GetInputSlot(1));
1497 cellStateIn.Connect(layer->GetInputSlot(2));
1498
1499 return (SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, 0, model, data) &&
1500 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 1, *layer, 1, model, data) &&
1501 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 2, *layer, 2, model, data) &&
1502 SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 3, *layer, 3, model, data));
1503}
1504
David Monahan613b49c2019-06-27 11:37:47 +01001505bool HalPolicy::ConvertTransposeConvolution2d(const Operation& operation, const Model& model, ConversionData& data)
1506{
1507 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
1508
1509 if (!input.IsValid())
1510 {
1511 return Fail("%s: Operation has invalid inputs", __func__);
1512 }
1513
1514 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
1515
1516 if (!output)
1517 {
1518 return Fail("%s: Could not read output 0", __func__);
1519 }
1520
1521 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1522 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1523 if (IsDynamicTensor(outputInfo))
1524 {
1525 return Fail("%s: Dynamic output tensors are not supported", __func__);
1526 }
1527
1528 // ArmNN does not currently support non-fixed weights or bias
1529 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
1530 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
1531
1532 if (weightsOperand == nullptr)
1533 {
1534 return Fail("%s: Operand is invalid", __func__);
1535 }
1536 armnn::TransposeConvolution2dDescriptor desc;
1537 desc.m_DataLayout = armnn::DataLayout::NHWC;
1538
1539 // Determine whether padding is implicit or explicit
1540 bool implicitPadding = operation.inputs.size() == 9;
1541
1542 if (implicitPadding )
1543 {
1544 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 8, model, data);
1545 }
1546 else
1547 {
1548 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
1549 }
1550
1551 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
1552 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1553 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1554
1555 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
1556
1557 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
1558 // We have to permute it to OIHW if the data layout is NCHW.
1559 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
1560 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
1561 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
1562
1563 // Bias is a 1D tensor
1564 const ConstTensorPin biasPin =
1565 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
1566
1567 if (!weightsPin.IsValid())
1568 {
1569 return Fail("%s: Operation has invalid weights", __func__);
1570 }
1571
1572 if (!biasPin.IsValid())
1573 {
1574 return Fail("%s: Operation has invalid biases", __func__);
1575 }
1576
1577 armnn::ConstTensor weights = weightsPin.GetConstTensor();
1578 armnn::ConstTensor bias = biasPin.GetConstTensor();
1579 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1580
1581 ActivationFn activation;
1582
1583 if (implicitPadding)
1584 {
1585 android::nn::PaddingScheme paddingScheme;
1586 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 4, paddingScheme, model, data) ||
1587 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideX, model, data) ||
1588 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_StrideY, model, data) ||
1589 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data))
1590 {
1591 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1592 }
1593
1594 const uint32_t kernelX = weights.GetShape()[widthIndex];
1595 const uint32_t kernelY = weights.GetShape()[heightIndex];
1596 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
1597 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
1598
1599 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1600 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1601 }
1602 else if (operation.inputs.size() == 11)
1603 {
1604 // explicit padding
1605 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
1606 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
1607 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
1608 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
1609 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
1610 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
1611 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data))
1612 {
1613 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1614 }
1615 }
1616 else
1617 {
1618 return Fail("%s: Unsupported number of operation inputs", __func__);
1619 }
1620
1621 desc.m_BiasEnabled = true;
1622 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1623
1624 bool isSupported = false;
1625 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1626 IsTransposeConvolution2dSupported,
1627 data.m_Backends,
1628 isSupported,
1629 inputInfo,
1630 outputInfo,
1631 desc,
1632 weights.GetInfo(),
1633 biases);
1634 if (!isSupported)
1635 {
1636 return false;
1637 }
1638
1639 armnn::IConnectableLayer* startLayer =
1640 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1641 if (!startLayer)
1642 {
1643 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
1644 }
1645
1646 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1647 if (!endLayer)
1648 {
1649 return Fail("%s: ProcessActivation failed", __func__);
1650 }
1651
1652 input.Connect(startLayer->GetInputSlot(0));
1653
1654 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
1655}
1656
Mike Kellyb5fdf382019-06-11 16:35:25 +01001657} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +01001658} // namespace armnn_driver