blob: 4e638cf461326d5ae28c434f363b781c799092de [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
8#include "../1.0/HalPolicy.hpp"
9#include "../1.1/HalPolicy.hpp"
10
11namespace armnn_driver
12{
13namespace hal_1_2
14{
15
16bool HandledByV1_0(V1_2::OperationType operationType)
17{
18 switch (static_cast<V1_0::OperationType>(operationType))
19 {
20 case V1_0::OperationType::ADD:
21 case V1_0::OperationType::AVERAGE_POOL_2D:
22 case V1_0::OperationType::CONCATENATION:
23 case V1_0::OperationType::DEPTH_TO_SPACE:
24 case V1_0::OperationType::DEQUANTIZE:
25 case V1_0::OperationType::EMBEDDING_LOOKUP:
26 case V1_0::OperationType::FLOOR:
27 case V1_0::OperationType::FULLY_CONNECTED:
28 case V1_0::OperationType::HASHTABLE_LOOKUP:
29 case V1_0::OperationType::L2_NORMALIZATION:
30 case V1_0::OperationType::L2_POOL_2D:
31 case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
32 case V1_0::OperationType::LOGISTIC:
33 case V1_0::OperationType::LSH_PROJECTION:
34 case V1_0::OperationType::LSTM:
35 case V1_0::OperationType::MAX_POOL_2D:
36 case V1_0::OperationType::MUL:
37 case V1_0::OperationType::RELU:
38 case V1_0::OperationType::RELU1:
39 case V1_0::OperationType::RELU6:
40 case V1_0::OperationType::RESHAPE:
41 case V1_0::OperationType::RESIZE_BILINEAR:
42 case V1_0::OperationType::RNN:
43 case V1_0::OperationType::SOFTMAX:
44 case V1_0::OperationType::SPACE_TO_DEPTH:
45 case V1_0::OperationType::SVDF:
46 case V1_0::OperationType::TANH:
47 case V1_0::OperationType::OEM_OPERATION:
48 return true;
49 default:
50 return false;
51 }
52}
53
54bool HandledByV1_1(V1_2::OperationType operationType)
55{
56 if (HandledByV1_0(operationType))
57 {
58 return true;
59 }
60 switch (static_cast<V1_1::OperationType>(operationType))
61 {
62 case V1_1::OperationType::BATCH_TO_SPACE_ND:
63 case V1_1::OperationType::DIV:
64 case V1_1::OperationType::MEAN:
65 case V1_1::OperationType::PAD:
66 case V1_1::OperationType::SPACE_TO_BATCH_ND:
67 case V1_1::OperationType::SQUEEZE:
68 case V1_1::OperationType::STRIDED_SLICE:
69 case V1_1::OperationType::SUB:
70 case V1_1::OperationType::TRANSPOSE:
71 return true;
72 default:
73 return false;
74 }
75}
76
77bool HandledByV1_0(const V1_2::Operation& operation)
78{
79 return HandledByV1_0(operation.type);
80}
81
82bool HandledByV1_1(const V1_2::Operation& operation)
83{
84 return HandledByV1_1(operation.type);
85}
86
87V1_0::OperationType CastToV1_0(V1_2::OperationType type)
88{
89 return static_cast<V1_0::OperationType>(type);
90}
91
92V1_1::OperationType CastToV1_1(V1_2::OperationType type)
93{
94 return static_cast<V1_1::OperationType>(type);
95}
96
97V1_0::Operation ConvertToV1_0(const V1_2::Operation& operation)
98{
99 V1_0::Operation op;
100 op.type = CastToV1_0(operation.type);
101 op.inputs = operation.inputs;
102 op.outputs = operation.outputs;
103 return op;
104}
105
106V1_1::Operation ConvertToV1_1(const V1_2::Operation& operation)
107{
108 V1_1::Operation op;
109 op.type = CastToV1_1(operation.type);
110 op.inputs = operation.inputs;
111 op.outputs = operation.outputs;
112 return op;
113}
114
115bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
116{
117 if (HandledByV1_0(operation) && compliantWithV1_0(model))
118 {
119 hal_1_0::HalPolicy::Operation v10Operation = ConvertToV1_0(operation);
120 hal_1_0::HalPolicy::Model v10Model = convertToV1_0(model);
121
122 return hal_1_0::HalPolicy::ConvertOperation(v10Operation, v10Model, data);
123 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100124
125 if (HandledByV1_1(operation) && compliantWithV1_1(model))
Mike Kellyb5fdf382019-06-11 16:35:25 +0100126 {
127 hal_1_1::HalPolicy::Operation v11Operation = ConvertToV1_1(operation);
128 hal_1_1::HalPolicy::Model v11Model = convertToV1_1(model);
129
130 return hal_1_1::HalPolicy::ConvertOperation(v11Operation, v11Model, data);
131 }
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100132
Mike Kellyb5fdf382019-06-11 16:35:25 +0100133 switch (operation.type)
134 {
135 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100136 return ConvertConv2d(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100137 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100138 return ConvertDepthwiseConv2d(operation, model, data);
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100139 case V1_2::OperationType::PRELU:
140 return ConvertPrelu(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100141 default:
142 return Fail("%s: Operation type %s not supported in ArmnnDriver",
143 __func__, toString(operation.type).c_str());
144 }
145}
146
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100147bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
148{
149 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
150 if (!input.IsValid())
151 {
152 return Fail("%s: Operation has invalid inputs", __func__);
153 }
154
155 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
156 if (!output)
157 {
158 return Fail("%s: Could not read output 0", __func__);
159 }
160
161 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
162 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
163
164 // ArmNN does not currently support non-fixed weights or bias
165 const ConstTensorPin weightsPin =
166 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
167 const ConstTensorPin biasPin =
168 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
169
170 if (!weightsPin.IsValid())
171 {
172 return Fail("%s: Operation has invalid weights", __func__);
173 }
174
175 if (!biasPin.IsValid())
176 {
177 return Fail("%s: Operation has invalid biases", __func__);
178 }
179
180 armnn::ConstTensor weights = weightsPin.GetConstTensor();
181 armnn::ConstTensor bias = biasPin.GetConstTensor();
182 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
183
184 armnn::Convolution2dDescriptor desc;
185 desc.m_DataLayout = armnn::DataLayout::NHWC;
186 ActivationFn activation;
187
188 // Determine whether padding is implicit or explicit
189 bool implicitPadding = operation.inputs.size() == 7 ||
190 (operation.inputs.size() >= 8 &&
191 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
192
193 if (implicitPadding)
194 {
195 android::nn::PaddingScheme paddingScheme;
196 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
197 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
198 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
199 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
200 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
201 {
202 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
203 }
204
205 const uint32_t kernelX = weights.GetShape()[2];
206 const uint32_t kernelY = weights.GetShape()[1];
207 const uint32_t inputX = inputInfo.GetShape()[2];
208 const uint32_t inputY = inputInfo.GetShape()[1];
209
210 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
211 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
212
213 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
214 }
215 else if (operation.inputs.size() >= 10)
216 {
217 // explicit padding
218 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
219 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
220 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
221 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
222 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
223 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
224 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
225 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
226 {
227 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
228 }
229 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
230 }
231 else
232 {
233 return Fail("%s: Unsupported number of operation inputs", __func__);
234 }
235
236 desc.m_BiasEnabled = true;
237 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
238
239 if (!IsLayerSupportedForAnyBackend(__func__,
240 armnn::IsConvolution2dSupported,
241 data.m_Backends,
242 inputInfo,
243 outputInfo,
244 desc,
245 weights.GetInfo(),
246 biases))
247 {
248 return false;
249 }
250
251 armnn::IConnectableLayer* startLayer =
252 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
253
254 if (!startLayer)
255 {
256 return Fail("%s: AddConvolution2dLayer failed", __func__);
257 }
258
259 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
260
261 if (!endLayer)
262 {
263 return Fail("%s: ProcessActivation failed", __func__);
264 }
265
266 input.Connect(startLayer->GetInputSlot(0));
267
268 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
269}
270
271bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
272{
273 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
274
275 if (!input.IsValid())
276 {
277 return Fail("%s: Operation has invalid inputs", __func__);
278 }
279
280 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
281
282 if (!output)
283 {
284 return Fail("%s: Could not read output 0", __func__);
285 }
286
287 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
288 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
289
290 // ArmNN does not currently support non-fixed weights or bias
291 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
292 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
293
294 if (weightsOperand == nullptr)
295 {
296 return Fail("%s: Operand is invalid", __func__);
297 }
298 armnn::DepthwiseConvolution2dDescriptor desc;
299 desc.m_DataLayout = armnn::DataLayout::NHWC;
300
301 // Determine whether padding is implicit or explicit
302 bool implicitPadding = operation.inputs.size() == 8 ||
303 (operation.inputs.size() >= 9 &&
304 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
305
306 // Look ahead to find the optional DataLayout, if present
307 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
308 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
309
310 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
311 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
312 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
313 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
314
315 // Reinterpret weight data as [ H, W, I, M ]
316 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
317 weightsOperand->dimensions[2],
318 inputInfo.GetShape()[channelsIndex],
319 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
320
321 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
322 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
323
324 const ConstTensorPin weightsPin =
325 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
326 1,
327 model,
328 data,
329 HWIMToMIHW,
330 &weightsShape);
331
332 // Bias is a 1D tensor
333 const ConstTensorPin biasPin =
334 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
335
336 if (!weightsPin.IsValid())
337 {
338 return Fail("%s: Operation has invalid weights", __func__);
339 }
340
341 if (!biasPin.IsValid())
342 {
343 return Fail("%s: Operation has invalid biases", __func__);
344 }
345
346 armnn::ConstTensor weights = weightsPin.GetConstTensor();
347 armnn::ConstTensor bias = biasPin.GetConstTensor();
348 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
349
350 ActivationFn activation;
351
352 if (implicitPadding)
353 {
354 android::nn::PaddingScheme paddingScheme;
355 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
356 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
357 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
358 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
359 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
360 {
361 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
362 }
363
364 const uint32_t kernelX = weights.GetShape()[3];
365 const uint32_t kernelY = weights.GetShape()[2];
366 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
367 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
368
369 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
370 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
371 }
372 else if (operation.inputs.size() >= 11)
373 {
374 // explicit padding
375 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
376 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
377 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
378 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
379 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
380 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
381 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
382 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
383 {
384 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
385 }
386 }
387 else
388 {
389 return Fail("%s: Unsupported number of operation inputs", __func__);
390 }
391
392 desc.m_BiasEnabled = true;
393 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
394
395 if (!IsLayerSupportedForAnyBackend(__func__,
396 armnn::IsDepthwiseConvolutionSupported,
397 data.m_Backends,
398 inputInfo,
399 outputInfo,
400 desc,
401 weights.GetInfo(),
402 biases))
403 {
404 return false;
405 }
406
407 armnn::IConnectableLayer* startLayer =
408 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
409 if (!startLayer)
410 {
411 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
412 }
413
414 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
415 if (!endLayer)
416 {
417 return Fail("%s: ProcessActivation failed", __func__);
418 }
419
420 input.Connect(startLayer->GetInputSlot(0));
421
422 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
423}
424
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100425bool HalPolicy::ConvertPrelu(const Operation& operation, const Model& model, ConversionData& data)
426{
427 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
428 LayerInputHandle alpha = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 1, model, data);
429
430 if (!input.IsValid() || !alpha.IsValid())
431 {
432 return Fail("%s: Operation has invalid inputs", __func__);
433 }
434
435 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
436
437 if (!output)
438 {
439 return Fail("%s: Could not read output 0", __func__);
440 }
441
442 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
443 const armnn::TensorInfo& alphaInfo = alpha.GetTensorInfo();
444 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
445
446 if (!IsLayerSupportedForAnyBackend(__func__,
447 armnn::IsPreluSupported,
448 data.m_Backends,
449 inputInfo,
450 alphaInfo,
451 outputInfo))
452 {
453 return false;
454 }
455
456 armnn::IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
457
458 if (!layer)
459 {
460 return Fail("%s: AddPreluLayer failed", __func__);
461 }
462
463 input.Connect(layer->GetInputSlot(0));
464 alpha.Connect(layer->GetInputSlot(1));
465
466 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *layer, model, data);
467}
468
Mike Kellyb5fdf382019-06-11 16:35:25 +0100469} // namespace hal_1_2
Matteo Martincigh17ffff32019-06-27 14:12:55 +0100470} // namespace armnn_driver