blob: b3b1d69feb0f9fc859baf299bbe6d0e5e9ead7f6 [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 }
124 else if (HandledByV1_1(operation) && compliantWithV1_1(model))
125 {
126 hal_1_1::HalPolicy::Operation v11Operation = ConvertToV1_1(operation);
127 hal_1_1::HalPolicy::Model v11Model = convertToV1_1(model);
128
129 return hal_1_1::HalPolicy::ConvertOperation(v11Operation, v11Model, data);
130 }
131 switch (operation.type)
132 {
133 case V1_2::OperationType::CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100134 return ConvertConv2d(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100135 case V1_2::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100136 return ConvertDepthwiseConv2d(operation, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +0100137 default:
138 return Fail("%s: Operation type %s not supported in ArmnnDriver",
139 __func__, toString(operation.type).c_str());
140 }
141}
142
Aron Virginas-Tar24e699d2019-06-17 14:47:46 +0100143bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
144{
145 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
146 if (!input.IsValid())
147 {
148 return Fail("%s: Operation has invalid inputs", __func__);
149 }
150
151 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
152 if (!output)
153 {
154 return Fail("%s: Could not read output 0", __func__);
155 }
156
157 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
158 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
159
160 // ArmNN does not currently support non-fixed weights or bias
161 const ConstTensorPin weightsPin =
162 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 1, model, data);
163 const ConstTensorPin biasPin =
164 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
165
166 if (!weightsPin.IsValid())
167 {
168 return Fail("%s: Operation has invalid weights", __func__);
169 }
170
171 if (!biasPin.IsValid())
172 {
173 return Fail("%s: Operation has invalid biases", __func__);
174 }
175
176 armnn::ConstTensor weights = weightsPin.GetConstTensor();
177 armnn::ConstTensor bias = biasPin.GetConstTensor();
178 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
179
180 armnn::Convolution2dDescriptor desc;
181 desc.m_DataLayout = armnn::DataLayout::NHWC;
182 ActivationFn activation;
183
184 // Determine whether padding is implicit or explicit
185 bool implicitPadding = operation.inputs.size() == 7 ||
186 (operation.inputs.size() >= 8 &&
187 GetInputOperand<hal_1_2::HalPolicy>(operation, 7, model)->type == OperandType::BOOL);
188
189 if (implicitPadding)
190 {
191 android::nn::PaddingScheme paddingScheme;
192 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
193 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
194 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
195 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 6, activation, model, data) ||
196 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 8, desc, model, data))
197 {
198 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
199 }
200
201 const uint32_t kernelX = weights.GetShape()[2];
202 const uint32_t kernelY = weights.GetShape()[1];
203 const uint32_t inputX = inputInfo.GetShape()[2];
204 const uint32_t inputY = inputInfo.GetShape()[1];
205
206 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
207 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
208
209 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 7, model, data);
210 }
211 else if (operation.inputs.size() >= 10)
212 {
213 // explicit padding
214 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
215 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
216 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
217 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
218 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
219 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
220 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 9, activation, model, data) ||
221 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 11, desc, model, data))
222 {
223 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
224 }
225 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, 10, model, data);
226 }
227 else
228 {
229 return Fail("%s: Unsupported number of operation inputs", __func__);
230 }
231
232 desc.m_BiasEnabled = true;
233 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
234
235 if (!IsLayerSupportedForAnyBackend(__func__,
236 armnn::IsConvolution2dSupported,
237 data.m_Backends,
238 inputInfo,
239 outputInfo,
240 desc,
241 weights.GetInfo(),
242 biases))
243 {
244 return false;
245 }
246
247 armnn::IConnectableLayer* startLayer =
248 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
249
250 if (!startLayer)
251 {
252 return Fail("%s: AddConvolution2dLayer failed", __func__);
253 }
254
255 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
256
257 if (!endLayer)
258 {
259 return Fail("%s: ProcessActivation failed", __func__);
260 }
261
262 input.Connect(startLayer->GetInputSlot(0));
263
264 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
265}
266
267bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
268{
269 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_2::HalPolicy>(operation, 0, model, data);
270
271 if (!input.IsValid())
272 {
273 return Fail("%s: Operation has invalid inputs", __func__);
274 }
275
276 const Operand* output = GetOutputOperand<hal_1_2::HalPolicy>(operation, 0, model);
277
278 if (!output)
279 {
280 return Fail("%s: Could not read output 0", __func__);
281 }
282
283 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
284 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
285
286 // ArmNN does not currently support non-fixed weights or bias
287 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
288 const Operand* weightsOperand = GetInputOperand<hal_1_2::HalPolicy>(operation, 1, model);
289
290 if (weightsOperand == nullptr)
291 {
292 return Fail("%s: Operand is invalid", __func__);
293 }
294 armnn::DepthwiseConvolution2dDescriptor desc;
295 desc.m_DataLayout = armnn::DataLayout::NHWC;
296
297 // Determine whether padding is implicit or explicit
298 bool implicitPadding = operation.inputs.size() == 8 ||
299 (operation.inputs.size() >= 9 &&
300 GetInputOperand<hal_1_2::HalPolicy>(operation, 8, model)->type == OperandType::BOOL);
301
302 // Look ahead to find the optional DataLayout, if present
303 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
304 desc.m_DataLayout = OptionalDataLayout<hal_1_2::HalPolicy>(operation, dataLayoutFlagIndex, model, data);
305
306 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
307 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
308 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
309 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
310
311 // Reinterpret weight data as [ H, W, I, M ]
312 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
313 weightsOperand->dimensions[2],
314 inputInfo.GetShape()[channelsIndex],
315 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
316
317 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
318 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
319
320 const ConstTensorPin weightsPin =
321 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation,
322 1,
323 model,
324 data,
325 HWIMToMIHW,
326 &weightsShape);
327
328 // Bias is a 1D tensor
329 const ConstTensorPin biasPin =
330 ConvertOperationInputToConstTensorPin<hal_1_2::HalPolicy>(operation, 2, model, data);
331
332 if (!weightsPin.IsValid())
333 {
334 return Fail("%s: Operation has invalid weights", __func__);
335 }
336
337 if (!biasPin.IsValid())
338 {
339 return Fail("%s: Operation has invalid biases", __func__);
340 }
341
342 armnn::ConstTensor weights = weightsPin.GetConstTensor();
343 armnn::ConstTensor bias = biasPin.GetConstTensor();
344 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
345
346 ActivationFn activation;
347
348 if (implicitPadding)
349 {
350 android::nn::PaddingScheme paddingScheme;
351 if (!GetInputPaddingScheme<hal_1_2::HalPolicy>(operation, 3, paddingScheme, model, data) ||
352 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
353 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
354 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 7, activation, model, data) ||
355 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 9, desc, model, data))
356 {
357 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
358 }
359
360 const uint32_t kernelX = weights.GetShape()[3];
361 const uint32_t kernelY = weights.GetShape()[2];
362 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
363 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
364
365 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
366 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
367 }
368 else if (operation.inputs.size() >= 11)
369 {
370 // explicit padding
371 if (!GetInputScalar<hal_1_2::HalPolicy>(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
372 !GetInputScalar<hal_1_2::HalPolicy>(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
373 !GetInputScalar<hal_1_2::HalPolicy>(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
374 !GetInputScalar<hal_1_2::HalPolicy>(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
375 !GetInputScalar<hal_1_2::HalPolicy>(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
376 !GetInputScalar<hal_1_2::HalPolicy>(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
377 !GetInputActivationFunction<hal_1_2::HalPolicy>(operation, 10, activation, model, data) ||
378 !GetOptionalConvolutionDilationParams<hal_1_2::HalPolicy>(operation, 12, desc, model, data))
379 {
380 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
381 }
382 }
383 else
384 {
385 return Fail("%s: Unsupported number of operation inputs", __func__);
386 }
387
388 desc.m_BiasEnabled = true;
389 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
390
391 if (!IsLayerSupportedForAnyBackend(__func__,
392 armnn::IsDepthwiseConvolutionSupported,
393 data.m_Backends,
394 inputInfo,
395 outputInfo,
396 desc,
397 weights.GetInfo(),
398 biases))
399 {
400 return false;
401 }
402
403 armnn::IConnectableLayer* startLayer =
404 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
405 if (!startLayer)
406 {
407 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
408 }
409
410 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
411 if (!endLayer)
412 {
413 return Fail("%s: ProcessActivation failed", __func__);
414 }
415
416 input.Connect(startLayer->GetInputSlot(0));
417
418 return SetupAndTrackLayerOutputSlot<hal_1_2::HalPolicy>(operation, 0, *endLayer, model, data);
419}
420
Mike Kellyb5fdf382019-06-11 16:35:25 +0100421} // namespace hal_1_2
422} // namespace armnn_driver