blob: 5b501dbc561edd98c5b99ff1c317b75bc6c9516c [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-Tarf03fcf02019-07-09 17:44:24 +01008#include "OutputShapeUtils.hpp"
9
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:
28 case V1_0::OperationType::AVERAGE_POOL_2D:
29 case V1_0::OperationType::CONCATENATION:
30 case V1_0::OperationType::DEPTH_TO_SPACE:
31 case V1_0::OperationType::DEQUANTIZE:
32 case V1_0::OperationType::EMBEDDING_LOOKUP:
33 case V1_0::OperationType::FLOOR:
34 case V1_0::OperationType::FULLY_CONNECTED:
35 case V1_0::OperationType::HASHTABLE_LOOKUP:
36 case V1_0::OperationType::L2_NORMALIZATION:
37 case V1_0::OperationType::L2_POOL_2D:
38 case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
39 case V1_0::OperationType::LOGISTIC:
40 case V1_0::OperationType::LSH_PROJECTION:
41 case V1_0::OperationType::LSTM:
42 case V1_0::OperationType::MAX_POOL_2D:
43 case V1_0::OperationType::MUL:
44 case V1_0::OperationType::RELU:
45 case V1_0::OperationType::RELU1:
46 case V1_0::OperationType::RELU6:
47 case V1_0::OperationType::RESHAPE:
Mike Kellyb5fdf382019-06-11 16:35:25 +010048 case V1_0::OperationType::RNN:
49 case V1_0::OperationType::SOFTMAX:
50 case V1_0::OperationType::SPACE_TO_DEPTH:
51 case V1_0::OperationType::SVDF:
52 case V1_0::OperationType::TANH:
53 case V1_0::OperationType::OEM_OPERATION:
54 return true;
55 default:
56 return false;
57 }
58}
59
60bool HandledByV1_1(V1_2::OperationType operationType)
61{
62 if (HandledByV1_0(operationType))
63 {
64 return true;
65 }
66 switch (static_cast<V1_1::OperationType>(operationType))
67 {
68 case V1_1::OperationType::BATCH_TO_SPACE_ND:
69 case V1_1::OperationType::DIV:
70 case V1_1::OperationType::MEAN:
71 case V1_1::OperationType::PAD:
72 case V1_1::OperationType::SPACE_TO_BATCH_ND:
73 case V1_1::OperationType::SQUEEZE:
74 case V1_1::OperationType::STRIDED_SLICE:
75 case V1_1::OperationType::SUB:
76 case V1_1::OperationType::TRANSPOSE:
77 return true;
78 default:
79 return false;
80 }
81}
82
83bool HandledByV1_0(const V1_2::Operation& operation)
84{
85 return HandledByV1_0(operation.type);
86}
87
88bool HandledByV1_1(const V1_2::Operation& operation)
89{
90 return HandledByV1_1(operation.type);
91}
92
93V1_0::OperationType CastToV1_0(V1_2::OperationType type)
94{
95 return static_cast<V1_0::OperationType>(type);
96}
97
98V1_1::OperationType CastToV1_1(V1_2::OperationType type)
99{
100 return static_cast<V1_1::OperationType>(type);
101}
102
103V1_0::Operation ConvertToV1_0(const V1_2::Operation& operation)
104{
105 V1_0::Operation op;
106 op.type = CastToV1_0(operation.type);
107 op.inputs = operation.inputs;
108 op.outputs = operation.outputs;
109 return op;
110}
111
112V1_1::Operation ConvertToV1_1(const V1_2::Operation& operation)
113{
114 V1_1::Operation op;
115 op.type = CastToV1_1(operation.type);
116 op.inputs = operation.inputs;
117 op.outputs = operation.outputs;
118 return op;
119}
120
121bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
122{
123 if (HandledByV1_0(operation) && compliantWithV1_0(model))
124 {
125 hal_1_0::HalPolicy::Operation v10Operation = ConvertToV1_0(operation);
126 hal_1_0::HalPolicy::Model v10Model = convertToV1_0(model);
127
128 return hal_1_0::HalPolicy::ConvertOperation(v10Operation, v10Model, data);
129 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100130
131 if (HandledByV1_1(operation) && compliantWithV1_1(model))
Mike Kellyb5fdf382019-06-11 16:35:25 +0100132 {
133 hal_1_1::HalPolicy::Operation v11Operation = ConvertToV1_1(operation);
134 hal_1_1::HalPolicy::Model v11Model = convertToV1_1(model);
135
136 return hal_1_1::HalPolicy::ConvertOperation(v11Operation, v11Model, data);
137 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100138
Mike Kellyb5fdf382019-06-11 16:35:25 +0100139 switch (operation.type)
140 {
141 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100142 return ConvertConv2d(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100143 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100144 return ConvertDepthwiseConv2d(operation, model, data);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100145 case V1_2::OperationType::MAXIMUM:
146 return ConvertMaximum(operation, model, data);
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100147 case V1_2::OperationType::PAD_V2:
148 return ConvertPadV2(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100149 case V1_2::OperationType::PRELU:
150 return ConvertPrelu(operation, model, data);
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100151 case V1_2::OperationType::RESIZE_BILINEAR:
152 return ConvertResize(operation, model, data, armnn::ResizeMethod::Bilinear);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100153 case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR:
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100154 return ConvertResize(operation, model, data, armnn::ResizeMethod::NearestNeighbor);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100155 default:
156 return Fail("%s: Operation type %s not supported in ArmnnDriver",
157 __func__, toString(operation.type).c_str());
158 }
159}
160
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100161bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
162{
163 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
164 if (!input.IsValid())
165 {
166 return Fail("%s: Operation has invalid inputs", __func__);
167 }
168
169 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
170 if (!output)
171 {
172 return Fail("%s: Could not read output 0", __func__);
173 }
174
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100175 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
176 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100177
Mike Kellye1d60bb2019-07-11 11:44:52 +0100178 armnn::Convolution2dDescriptor desc;
179 desc.m_DataLayout = armnn::DataLayout::NHWC;
180
181 // Determine whether padding is implicit or explicit
182 bool implicitPadding = operation.inputs.size() == 7 ||
183 (operation.inputs.size() >= 8 &&
184 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
185
186 if (implicitPadding)
187 {
188 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
189 }
190 else if (operation.inputs.size() >= 10)
191 {
192 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
193 }
194
195 const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1};
196
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100197 // ArmNN does not currently support non-fixed weights or bias
Mike Kellye1d60bb2019-07-11 11:44:52 +0100198 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
199 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
200 // the DataLayout is NCHW
201 const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ?
202 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
203 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100204 const ConstTensorPin biasPin =
Mike Kellye1d60bb2019-07-11 11:44:52 +0100205 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100206
207 if (!weightsPin.IsValid())
208 {
209 return Fail("%s: Operation has invalid weights", __func__);
210 }
211
212 if (!biasPin.IsValid())
213 {
214 return Fail("%s: Operation has invalid biases", __func__);
215 }
216
217 armnn::ConstTensor weights = weightsPin.GetConstTensor();
218 armnn::ConstTensor bias = biasPin.GetConstTensor();
219 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
220
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100221 ActivationFn activation;
222
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100223 if (implicitPadding)
224 {
225 android::nn::PaddingScheme paddingScheme;
226 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
227 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
228 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
229 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
230 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
231 {
232 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
233 }
234
Mike Kellye1d60bb2019-07-11 11:44:52 +0100235 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
236 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
237 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
238 const uint32_t kernelX = weights.GetShape()[widthIndex];
239 const uint32_t kernelY = weights.GetShape()[heightIndex];
240 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
241 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100242
Mike Kelly86b36d42019-07-12 16:39:33 +0100243 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
244 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100245
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100246 }
247 else if (operation.inputs.size() >= 10)
248 {
249 // explicit padding
250 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
251 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
252 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
253 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
254 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
255 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
256 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
257 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
258 {
259 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
260 }
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100261 }
262 else
263 {
264 return Fail("%s: Unsupported number of operation inputs", __func__);
265 }
266
267 desc.m_BiasEnabled = true;
268 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
269
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100270 if (IsDynamicOutput(outputInfo))
271 {
272 try
273 {
274 ALOGD("Output shape not set, will infer from inputs");
275 outputInfo.SetShape(InferConvolution2dOutputShape(inputInfo.GetShape(),
276 weights.GetInfo().GetShape(),
277 desc));
278 }
279 catch (armnn::Exception& e)
280 {
281 return Fail("%s: Could not infer dynamic output shape: %s", __func__, e.what());
282 }
283 }
284
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100285 bool isSupported = false;
286 FORWARD_LAYER_SUPPORT_FUNC(__func__,
287 IsConvolution2dSupported,
288 data.m_Backends,
289 isSupported,
290 inputInfo,
291 outputInfo,
292 desc,
293 weights.GetInfo(),
294 biases);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100295
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100296 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100297 {
298 return false;
299 }
300
301 armnn::IConnectableLayer* startLayer =
302 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
303
304 if (!startLayer)
305 {
306 return Fail("%s: AddConvolution2dLayer failed", __func__);
307 }
308
309 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
310
311 if (!endLayer)
312 {
313 return Fail("%s: ProcessActivation failed", __func__);
314 }
315
316 input.Connect(startLayer->GetInputSlot(0));
317
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100318 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation,
319 0,
320 *endLayer,
321 model,
322 data,
323 armnn::Optional<armnn::TensorInfo>(outputInfo));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100324}
325
326bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
327{
328 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
329
330 if (!input.IsValid())
331 {
332 return Fail("%s: Operation has invalid inputs", __func__);
333 }
334
335 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
336
337 if (!output)
338 {
339 return Fail("%s: Could not read output 0", __func__);
340 }
341
342 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100343
344 // ArmNN does not currently support non-fixed weights or bias
345 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
346 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
347
348 if (weightsOperand == nullptr)
349 {
350 return Fail("%s: Operand is invalid", __func__);
351 }
352 armnn::DepthwiseConvolution2dDescriptor desc;
353 desc.m_DataLayout = armnn::DataLayout::NHWC;
354
355 // Determine whether padding is implicit or explicit
356 bool implicitPadding = operation.inputs.size() == 8 ||
357 (operation.inputs.size() >= 9 &&
358 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
359
360 // Look ahead to find the optional DataLayout, if present
361 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
362 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
363
364 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
365 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
366 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
367 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
368
369 // Reinterpret weight data as [ H, W, I, M ]
370 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
371 weightsOperand->dimensions[2],
372 inputInfo.GetShape()[channelsIndex],
373 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
374
375 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
376 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
377
378 const ConstTensorPin weightsPin =
379 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
380 1,
381 model,
382 data,
383 HWIMToMIHW,
384 &weightsShape);
385
386 // Bias is a 1D tensor
387 const ConstTensorPin biasPin =
388 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
389
390 if (!weightsPin.IsValid())
391 {
392 return Fail("%s: Operation has invalid weights", __func__);
393 }
394
395 if (!biasPin.IsValid())
396 {
397 return Fail("%s: Operation has invalid biases", __func__);
398 }
399
400 armnn::ConstTensor weights = weightsPin.GetConstTensor();
401 armnn::ConstTensor bias = biasPin.GetConstTensor();
402 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
403
404 ActivationFn activation;
405
406 if (implicitPadding)
407 {
408 android::nn::PaddingScheme paddingScheme;
409 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
410 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
411 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
412 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
413 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
414 {
415 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
416 }
417
418 const uint32_t kernelX = weights.GetShape()[3];
419 const uint32_t kernelY = weights.GetShape()[2];
420 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
421 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
422
Mike Kelly86b36d42019-07-12 16:39:33 +0100423 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
424 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100425 }
426 else if (operation.inputs.size() >= 11)
427 {
428 // explicit padding
429 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
430 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
431 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
432 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
433 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
434 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
435 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
436 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
437 {
438 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
439 }
440 }
441 else
442 {
443 return Fail("%s: Unsupported number of operation inputs", __func__);
444 }
445
446 desc.m_BiasEnabled = true;
447 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
448
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100449 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*output);
450 if (IsDynamicOutput(outputInfo))
451 {
452 try
453 {
454 ALOGD("Output shape not set, will infer from inputs");
455 outputInfo.SetShape(InferDepthwiseConvolution2dOutputShape(inputInfo.GetShape(),
456 weights.GetInfo().GetShape(),
457 desc));
458 }
459 catch (armnn::Exception& e)
460 {
461 return Fail("%s: Could not infer dynamic output shape: %s", __func__, e.what());
462 }
463 }
464
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100465 bool isSupported = false;
466 FORWARD_LAYER_SUPPORT_FUNC(__func__,
467 IsDepthwiseConvolutionSupported,
468 data.m_Backends,
469 isSupported,
470 inputInfo,
471 outputInfo,
472 desc,
473 weights.GetInfo(),
474 biases);
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100475
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100476 if (!isSupported)
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100477 {
478 return false;
479 }
480
481 armnn::IConnectableLayer* startLayer =
482 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100483
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100484 if (!startLayer)
485 {
486 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
487 }
488
489 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
490 if (!endLayer)
491 {
492 return Fail("%s: ProcessActivation failed", __func__);
493 }
494
495 input.Connect(startLayer->GetInputSlot(0));
496
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100497 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation,
498 0,
499 *endLayer,
500 model,
501 data,
502 armnn::Optional<armnn::TensorInfo>(outputInfo));
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100503}
504
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100505bool HalPolicy::ConvertMaximum(const Operation& operation, const Model& model, ConversionData& data)
506{
507 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
508 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
509
510 if (!input0.IsValid() || !input1.IsValid())
511 {
512 return Fail("%s: Operation has invalid inputs", __func__);
513 }
514
515 const Operand* outputOperand = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
516 if (!outputOperand)
517 {
518 return Fail("%s: Could not read output", __func__);
519 }
520
Aron Virginas-Tard7593232019-07-16 13:17:06 +0100521 armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100522 if (IsDynamicOutput(outInfo))
523 {
524 ALOGD("Output shape not set, will infer from inputs");
525 outInfo.SetShape(InferMaximumOutputShape(input0.GetTensorInfo().GetShape(), input1.GetTensorInfo().GetShape()));
526 }
527
Aron Virginas-Tard7593232019-07-16 13:17:06 +0100528 bool isSupported = false;
529 FORWARD_LAYER_SUPPORT_FUNC(__func__,
530 IsMaximumSupported,
531 data.m_Backends,
532 isSupported,
533 input0.GetTensorInfo(),
534 input1.GetTensorInfo(),
535 outInfo);
536
537 if (!isSupported)
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100538 {
539 return false;
540 }
541
542 armnn::IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
543 assert(layer != nullptr);
544 BroadcastTensor(input0, input1, layer, *data.m_Network);
545
546 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation,
547 0,
548 *layer,
549 model,
550 data,
551 armnn::Optional<armnn::TensorInfo>(outInfo));
552}
553
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100554bool HalPolicy::ConvertPadV2(const Operation& operation, const Model& model, ConversionData& data)
555{
556 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
557 if (!input.IsValid())
558 {
559 return Fail("%s: Could not read input 0", __func__);
560 }
561
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100562 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
563 if (!output)
564 {
565 return Fail("%s: Could not read output", __func__);
566 }
567
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100568 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
569 unsigned int rank = inputInfo.GetNumDimensions();
570
571 armnn::PadDescriptor descriptor;
572 if (!ConvertPaddings<hal_1_2::HalPolicy>(operation, model, data, rank, descriptor))
573 {
574 return Fail("%s: Could not convert paddings", __func__);
575 }
576
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100577 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*output);
578 if (IsDynamicOutput(outputInfo))
579 {
580 ALOGD("Output shape not set, will infer from inputs");
581 outputInfo.SetShape(InferPadOutputShape(inputInfo.GetShape(), descriptor.m_PadList));
582 }
583
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100584 // Determine type of padding value
585 OperandType operandType0;
586 OperandType operandType2;
587
588 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 0, model, operandType0) ||
589 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
590 {
591 return Fail("%s: Operation has invalid inputs", __func__);
592 }
593
594 // Read value to use for padding
595 if (operandType0 == OperandType::TENSOR_FLOAT16 && operandType2 == OperandType::FLOAT16)
596 {
597 armnn::Half f16PadValue;
598 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
599 {
600 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
601 }
602
603 descriptor.m_PadValue = f16PadValue;
604 }
605 else if (operandType0 == OperandType::TENSOR_FLOAT32 && operandType2 == OperandType::FLOAT32)
606 {
607 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
608 {
609 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
610 }
611 }
612 else if (operandType0 == OperandType::TENSOR_QUANT8_ASYMM && operandType2 == OperandType::INT32)
613 {
614 int32_t quantizedPadValue = 0;
615 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 2, quantizedPadValue, model, data))
616 {
617 return Fail("%s: Could not read input 2 (INT32)", __func__);
618 }
619
620 descriptor.m_PadValue = armnn::Dequantize(quantizedPadValue,
621 inputInfo.GetQuantizationScale(),
622 inputInfo.GetQuantizationOffset());
623 }
624 else
625 {
626 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
627 }
628
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100629 bool isSupported = false;
630 FORWARD_LAYER_SUPPORT_FUNC(__func__,
631 IsPadSupported,
632 data.m_Backends,
633 isSupported,
634 inputInfo,
635 outputInfo,
636 descriptor);
637 if (!isSupported)
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100638 {
639 return false;
640 }
641
642 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
643 assert(layer != nullptr);
644 input.Connect(layer->GetInputSlot(0));
645 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
646
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100647 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation,
648 0,
649 *layer,
650 model,
651 data,
652 armnn::Optional<armnn::TensorInfo>(outputInfo));
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +0100653}
654
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100655bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
656{
657 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
658 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
659
660 if (!input.IsValid() || !alpha.IsValid())
661 {
662 return Fail("%s: Operation has invalid inputs", __func__);
663 }
664
665 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
666
667 if (!output)
668 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100669 return Fail("%s: Could not read output", __func__);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100670 }
671
672 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
673 const armnn::TensorInfo& alphaInfo = alpha.GetTensorInfo();
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100674
675 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100676 if (IsDynamicOutput(outputInfo))
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100677 {
678 ALOGD("Output shape not set, will infer from inputs");
679 outputInfo.SetShape(InferPreluOutputShape(inputInfo.GetShape(), alphaInfo.GetShape()));
680 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100681
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100682 bool isSupported = false;
683 FORWARD_LAYER_SUPPORT_FUNC(__func__,
684 IsPreluSupported,
685 data.m_Backends,
686 isSupported,
687 inputInfo,
688 alphaInfo,
689 outputInfo);
690 if (!isSupported)
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100691 {
692 return false;
693 }
694
695 armnn::IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
696
697 if (!layer)
698 {
699 return Fail("%s: AddPreluLayer failed", __func__);
700 }
701
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100702 BroadcastTensor(input, alpha, layer, *data.m_Network);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100703
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100704 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation,
705 0,
706 *layer,
707 model,
708 data,
709 armnn::Optional<armnn::TensorInfo>(outputInfo));
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100710}
711
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100712bool HalPolicy::ConvertResize(const Operation& operation,
713 const Model& model,
714 ConversionData& data,
715 armnn::ResizeMethod resizeMethod)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100716{
717 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
718 if (!input.IsValid())
719 {
720 return Fail("%s: Could not read input 0", __func__);
721 }
722
723 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
724 if (!output)
725 {
726 return Fail("%s: Could not read output 0", __func__);
727 }
728
729 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +0100730 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100731
732 armnn::ResizeDescriptor descriptor;
Aron Virginas-Tarfb2fa292019-07-04 11:59:48 +0100733 descriptor.m_Method = resizeMethod;
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100734 descriptor.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 3, model, data);
735
736 OperandType operandType1;
737 OperandType operandType2;
738
739 if (!GetOperandType<hal_1_2::HalPolicy>(operation, 1, model, operandType1) ||
740 !GetOperandType<hal_1_2::HalPolicy>(operation, 2, model, operandType2))
741 {
742 return Fail("%s: Operation has invalid inputs", __func__);
743 }
744
745 if (operandType1 != operandType2)
746 {
747 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
748 }
749
750 if (operandType1 == OperandType::INT32)
751 {
752 // Case 1: resizing by shape
753 int32_t targetWidth = 0;
754 int32_t targetHeight = 0;
755
756 if (!GetInputInt32<hal_1_2::HalPolicy>(operation, 1, targetWidth, model, data) ||
757 !GetInputInt32<hal_1_2::HalPolicy>(operation, 2, targetHeight, model, data))
758 {
759 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
760 }
761
762 if (targetWidth < 0 || targetHeight < 0)
763 {
764 return Fail("%s: Operation has invalid inputs for resizing by shape. "
765 "Target width/height cannot be < 0", __func__);
766 }
767
768 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
769 descriptor.m_TargetWidth = static_cast<uint32_t>(targetHeight);
770 }
771 else if (operandType1 == OperandType::FLOAT32)
772 {
773 // Case 2: resizing by scale
774 float widthScale = 1.0f;
775 float heightScale = 1.0f;
776
777 if (!GetInputFloat32<hal_1_2::HalPolicy>(operation, 1, widthScale, model, data) ||
778 !GetInputFloat32<hal_1_2::HalPolicy>(operation, 2, heightScale, model, data))
779 {
780 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
781 }
782
783 const armnn::TensorShape& inputShape = inputInfo.GetShape();
784 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
785
786 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
787 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
788
789 descriptor.m_TargetWidth = std::floor(width * widthScale);
790 descriptor.m_TargetHeight = std::floor(height * heightScale);
791 }
792 else
793 {
794 // NOTE: FLOAT16 scales are not supported
795 return false;
796 }
797
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +0100798 if (IsDynamicOutput(outputInfo))
799 {
800 try
801 {
802 ALOGD("Output shape not set, will infer from inputs");
803 outputInfo.SetShape(InferResizeOutputShape(inputInfo.GetShape(), descriptor));
804 }
805 catch (armnn::Exception& e)
806 {
807 return Fail("%s: Could not infer dynamic output shape: %s", __func__, e.what());
808 }
809 }
810
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100811 bool isSupported = false;
812 FORWARD_LAYER_SUPPORT_FUNC(__func__,
813 IsResizeSupported,
814 data.m_Backends,
815 isSupported,
816 inputInfo,
817 outputInfo,
818 descriptor);
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +0100819
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100820 if (!isSupported)
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100821 {
822 return false;
823 }
824
825 armnn::IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
826
827 assert(layer != nullptr);
828
829 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
830 input.Connect(layer->GetInputSlot(0));
831
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +0100832 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation,
833 0,
834 *layer,
835 model,
836 data,
837 armnn::Optional<armnn::TensorInfo>(outputInfo));
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100838}
839
Keith Davisa6bc52f2019-06-26 09:39:49 +0100840bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
841{
842 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
843
844 if (!input.IsValid() )
845 {
846 return Fail("%s: Operation has invalid inputs", __func__);
847 }
848
849 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
850 unsigned int rank = inputInfo.GetNumDimensions();
851
852 if (rank != 4)
853 {
854 return Fail("%s: Only inputs with rank 4 are supported", __func__);
855 }
856
857 armnn::SpaceToDepthDescriptor desc;
858
859 GetInputScalar<hal_1_2::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
860
861 if (desc.m_BlockSize <= 1)
862 {
863 return Fail("%s: Block size must be at least 1 in all dimensions");
864 }
865
866 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 2, model, data);
867
868 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
869 if (!output)
870 {
871 return Fail("%s: Could not read output 0", __func__);
872 }
873
874 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100875
876 bool isSupported = false;
877 FORWARD_LAYER_SUPPORT_FUNC(__func__,
878 IsSpaceToDepthSupported,
879 data.m_Backends,
880 isSupported,
881 inputInfo,
882 outputInfo,
883 desc);
884 if (!isSupported)
Keith Davisa6bc52f2019-06-26 09:39:49 +0100885 {
886 return false;
887 }
888
889 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
890 assert(layer != nullptr);
891 input.Connect(layer->GetInputSlot(0));
892
893 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
894}
895
Mike Kellyb5fdf382019-06-11 16:35:25 +0100896} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100897} // namespace armnn_driver