blob: d0bd95bb77c7b6b2c3e7f014b28124e29bae1938 [file] [log] [blame]
arovir01b0717b52018-09-05 17:03:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "HalPolicy.hpp"
7
arovir015602b192018-10-04 16:15:02 +01008#include "armnn/Optional.hpp"
9
arovir01b0717b52018-09-05 17:03:25 +010010namespace armnn_driver
11{
12namespace hal_1_0
13{
14
15bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
16{
17 switch (operation.type)
18 {
19 case V1_0::OperationType::ADD:
20 return ConvertAdd(operation, model, data);
21 case V1_0::OperationType::AVERAGE_POOL_2D:
22 return ConvertAveragePool2d(operation, model, data);
23 case V1_0::OperationType::CONCATENATION:
24 return ConvertConcatenation(operation, model, data);
25 case V1_0::OperationType::CONV_2D:
26 return ConvertConv2d(operation, model, data);
27 case V1_0::OperationType::DEPTHWISE_CONV_2D:
28 return ConvertDepthwiseConv2d(operation, model, data);
29 case V1_0::OperationType::FLOOR:
30 return ConvertFloor(operation, model, data);
31 case V1_0::OperationType::FULLY_CONNECTED:
32 return ConvertFullyConnected(operation, model, data);
33 case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
34 return ConvertLocalResponseNormalization(operation, model, data);
35 case V1_0::OperationType::LOGISTIC:
36 return ConvertLogistic(operation, model, data);
37 case V1_0::OperationType::LSTM:
38 return ConvertLstm(operation, model, data);
39 case V1_0::OperationType::L2_NORMALIZATION:
40 return ConvertL2Normalization(operation, model, data);
41 case V1_0::OperationType::L2_POOL_2D:
42 return ConvertL2Pool2d(operation, model, data);
43 case V1_0::OperationType::MAX_POOL_2D:
44 return ConvertMaxPool2d(operation, model, data);
45 case V1_0::OperationType::MUL:
46 return ConvertMul(operation, model, data);
47 case V1_0::OperationType::RELU:
48 return ConvertReLu(operation, model, data);
49 case V1_0::OperationType::RELU1:
50 return ConvertReLu1(operation, model, data);
51 case V1_0::OperationType::RELU6:
52 return ConvertReLu6(operation, model, data);
53 case V1_0::OperationType::SOFTMAX:
54 return ConvertSoftmax(operation, model, data);
55 case V1_0::OperationType::TANH:
56 return ConvertTanH(operation, model, data);
57 case V1_0::OperationType::RESHAPE:
58 return ConvertReshape(operation, model, data);
59 case V1_0::OperationType::RESIZE_BILINEAR:
60 return ConvertResizeBilinear(operation, model, data);
61 default:
62 return Fail("%s: Operation type %s not supported in ArmnnDriver",
63 __func__, toString(operation.type).c_str());
64 }
65}
66
67bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
68{
69 LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data);
70 LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data);
71
72 if (!input0.IsValid() || !input1.IsValid())
73 {
74 return Fail("%s: Operation has invalid inputs", __func__);
75 }
76
77 // The FuseActivation parameter is always the input index 2
78 // and it should be optional
79 ActivationFn activationFunction;
80 if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data))
81 {
82 return Fail("%s: Operation has invalid inputs", __func__);
83 }
84
85 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
86 if (!outputOperand)
87 {
88 return false;
89 }
90
91 const armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
92
93 if (!IsLayerSupported(__func__,
94 armnn::IsAdditionSupported,
95 data.m_Compute,
96 input0.GetTensorInfo(),
97 input1.GetTensorInfo(),
98 outInfo))
99 {
100 return false;
101 }
102
103 armnn::IConnectableLayer* const startLayer = data.m_Network->AddAdditionLayer();
104 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
105
106 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
107 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
108
109 if (endLayer != nullptr)
110 {
111 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
112 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
113 }
114 else
115 {
116 return Fail("%s: ProcessActivation failed", __func__);
117 }
118}
119
120bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
121{
122 return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::Average, model, data);
123}
124
125bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
126{
127 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
128 if (operation.inputs.size() <= 1)
129 {
130 return Fail("%s: Operation has insufficient arguments", __func__);
131 }
132
133 // Get inputs and outputs
134 const std::size_t numInputTensors = operation.inputs.size() - 1;
135
136 int32_t concatDim;
137 if (!GetInputScalar(operation, numInputTensors, OperandType::INT32, concatDim, model, data))
138 {
139 return Fail("%s: Operation has invalid inputs", __func__);
140 }
141
142 const Operand* const outputOperand = GetOutputOperand(operation, 0, model);
143 if (!outputOperand)
144 {
145 return Fail("%s: Operation has no outputs", __func__);
146 }
147
148
149 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
150 armnn::TensorShape outputShape = outputInfo.GetShape();
151
152 //
153 // handle negative concat dims along the lines of tensorflow as described here:
154 // https://www.tensorflow.org/api_docs/python/tf/concat
155 // "negative axis refers to axis + rank(values)-th dimension"
156 //
157 if (concatDim < 0)
158 {
159 concatDim += outputShape.GetNumDimensions();
160 }
161
162 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
163 {
164 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
165 }
166
167 std::vector<LayerInputHandle> inputHandles;
168 std::vector<armnn::TensorShape> inputShapes;
169
170 inputHandles.reserve(numInputTensors);
171 inputShapes.reserve(numInputTensors);
172
173 bool inputsHaveBeenReshaped = false;
174 unsigned int tensorDimensionsAdded = 0;
175
176 for (uint32_t i = 0; i < numInputTensors; ++i)
177 {
178 const Operand* const operand = GetInputOperand(operation, i, model);
179 if (!operand)
180 {
181 return Fail("%s: Operation has invalid inputs", __func__);
182 }
183
184 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
185 LayerInputHandle operandInputHandle = ConvertToLayerInputHandle(operation, i, model, data);
186
187 if (operandShape.GetNumDimensions() == 0)
188 {
189 return Fail("%s: Operands with rank 0 are not supported", __func__);
190 }
191
192 if (RequiresReshape(operandShape))
193 {
194 inputsHaveBeenReshaped = true;
195
196 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
197
198 // Expand the tensor to three dimensions
199 if (operandShape.GetNumDimensions() == 2)
200 {
201 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
202 tensorDimensionsAdded = 1;
203 }
204 else
205 {
206 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
207 tensorDimensionsAdded = 2;
208 }
209
210 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
211 *data.m_Network,
212 operandInputHandle,
213 reshapeInfo
214 );
215
216 // Point to the reshape operation rather then the input operation
217 operandShape = reshapeInfo.GetShape();
218 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
219 }
220
221 inputShapes.emplace_back(operandShape);
222 inputHandles.emplace_back(operandInputHandle);
223
224 if (!inputHandles.back().IsValid())
225 {
226 return Fail("%s: Operation has invalid inputs", __func__);
227 }
228 }
229
230 BOOST_ASSERT(inputShapes.size() == inputHandles.size());
231
232 if (inputsHaveBeenReshaped)
233 {
234 // Adjust the concatenation dimension by the amount of dimensions added (if any)
235 concatDim += tensorDimensionsAdded;
236
237 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
238 if (tensorDimensionsAdded == 1)
239 {
240 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
241 }
242 else if (tensorDimensionsAdded == 2)
243 {
244 outputShape = armnn::TensorShape({1, 1, outputShape[0], outputShape[1]});
245 }
246 }
247
248 // Get the pair of permutations required for the concatenation
249 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
250 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
251
252 CreatePermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
253
254 outputShape = armnnUtils::Permuted(outputShape, permutationPair.first);
255 outputInfo.SetShape(outputShape);
256
257 // this is no-op for identity swizzles, otherwise it replaces both
258 // the handles and shapes with the swizzled layer output handles and shapes
259 SwizzleInputs(*data.m_Network, inputHandles, inputShapes, permutationPair.first);
260
261 // Create an armnn merger layer descriptor - this will also perform validation on the input shapes
262 armnn::OriginsDescriptor mergerDescriptor;
263 try
264 {
265 // The merger descriptor is always created across the only supported concat
266 // dimension, which is 0 or 1
267 mergerDescriptor =
268 armnn::CreateMergerDescriptorForConcatenation(
269 inputShapes.begin(), inputShapes.end(), concatDim);
270 }
271 catch (const armnn::Exception& error)
272 {
273 return Fail("%s: Error preparing merger descriptor. %s", __func__, error.what());
274 }
275
276 // Validate the output shape is correct given the input shapes based on the
277 // only valid concat dimension which is 0 or 1
278 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
279 {
280 return Fail("%s: Error validating the output shape for concat", __func__);
281 }
282
283 std::vector<const armnn::TensorInfo*> inputTensorInfos;
284 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
285 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
286 if (!IsLayerSupported(__func__,
287 armnn::IsMergerSupported,
288 data.m_Compute,
289 inputTensorInfos,
290 mergerDescriptor))
291 {
292 return false;
293 }
294
295 armnn::IConnectableLayer* layer = data.m_Network->AddMergerLayer(mergerDescriptor);
296 assert(layer != nullptr);
297 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
298
299 // Connect inputs to the layer
300 const int numInputSlots = layer->GetNumInputSlots();
301 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
302 for (int i = 0; i < numInputSlots; ++i)
303 {
304 // connect the input directly to the merge (concat) layer
305 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
306 }
307
308 // Add permutation layer and connect the output to it, the permutation becomes the output layer
309 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(*data.m_Network,
310 layer->GetOutputSlot(0),
311 permutationPair.second);
312 layer = &deswizzleLayer;
313
314 if (inputsHaveBeenReshaped)
315 {
316 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
317
318 // Undo the reshape knowing the amount of dimensions added
319 if (tensorDimensionsAdded == 1)
320 {
321 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
322 afterConcatInfo.GetShape()[2] }));
323 }
324 else if (tensorDimensionsAdded == 2)
325 {
326 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2],
327 afterConcatInfo.GetShape()[3] }));
328 }
329
330 layer = &AddReshapeLayer(
331 *data.m_Network,
332 layer->GetOutputSlot(0),
333 afterConcatInfo
334 );
335 }
336
337 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
338}
339
340bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
341{
342 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
343 if (!input.IsValid())
344 {
345 return Fail("%s: Operation has invalid inputs", __func__);
346 }
347
348 const Operand* output = GetOutputOperand(operation, 0, model);
349 if (!output)
350 {
351 return Fail("%s: Could not read output 0", __func__);
352 }
353
354 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
355 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
356
arovir01b0717b52018-09-05 17:03:25 +0100357 // ArmNN does not currently support non-fixed weights or bias
narpra01fb60a562018-10-30 15:46:01 +0000358 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100359 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data);
360
361 if (!weightsPin.IsValid() || !biasPin.IsValid())
362 {
363 return Fail("%s: Operation has invalid inputs", __func__);
364 }
365
366 armnn::ConstTensor weights = weightsPin.GetConstTensor();
367 armnn::ConstTensor bias = biasPin.GetConstTensor();
narpra01fb60a562018-10-30 15:46:01 +0000368 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
arovir01b0717b52018-09-05 17:03:25 +0100369
370 armnn::Convolution2dDescriptor desc;
narpra01fb60a562018-10-30 15:46:01 +0000371 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +0100372 ActivationFn activation;
373
374 if (operation.inputs.size() == 10)
375 {
376 if (!GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
377 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
378 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
379 !GetInputScalar(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
380 !GetInputScalar(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
381 !GetInputScalar(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
382 !GetInputActivationFunction(operation, 9, activation, model, data))
383 {
384 return Fail("%s: Operation has invalid inputs", __func__);
385 }
386 }
387 else if (operation.inputs.size() == 7)
388 {
389 android::nn::PaddingScheme paddingScheme;
390 if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) ||
391 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
392 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
393 !GetInputActivationFunction(operation, 6, activation, model, data))
394 {
395 return Fail("%s: Operation has invalid inputs", __func__);
396 }
397
narpra01fb60a562018-10-30 15:46:01 +0000398 const uint32_t kernelX = weights.GetShape()[2];
399 const uint32_t kernelY = weights.GetShape()[1];
400 const uint32_t inputX = inputInfo.GetShape()[2];
401 const uint32_t inputY = inputInfo.GetShape()[1];
arovir01b0717b52018-09-05 17:03:25 +0100402
403 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
404 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
405 }
406 else
407 {
408 return Fail("%s: Unsupported number of operation inputs", __func__);
409 }
410
411 desc.m_BiasEnabled = true;
arovir015602b192018-10-04 16:15:02 +0100412 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100413
414 if (!IsLayerSupported(__func__,
415 armnn::IsConvolution2dSupported,
416 data.m_Compute,
narpra01fb60a562018-10-30 15:46:01 +0000417 inputInfo,
418 outputInfo,
arovir01b0717b52018-09-05 17:03:25 +0100419 desc,
420 weights.GetInfo(),
421 biases))
422 {
423 return false;
424 }
425
426 armnn::IConnectableLayer* startLayer = data.m_Network->AddConvolution2dLayer(desc, weights, bias);
arovir01b0717b52018-09-05 17:03:25 +0100427
narpra01fb60a562018-10-30 15:46:01 +0000428 if (!startLayer)
arovir01b0717b52018-09-05 17:03:25 +0100429 {
narpra01fb60a562018-10-30 15:46:01 +0000430 return Fail("%s: AddConvolution2dLayer failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +0100431 }
narpra01fb60a562018-10-30 15:46:01 +0000432
433 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
434
435 if (!endLayer)
arovir01b0717b52018-09-05 17:03:25 +0100436 {
437 return Fail("%s: ProcessActivation failed", __func__);
438 }
narpra01fb60a562018-10-30 15:46:01 +0000439
440 input.Connect(startLayer->GetInputSlot(0));
441
442 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100443}
444
445bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
446{
447 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
448 if (!input.IsValid())
449 {
450 return Fail("%s: Operation has invalid inputs", __func__);
451 }
452
453 const Operand* output = GetOutputOperand(operation, 0, model);
454 if (!output)
455 {
456 return Fail("%s: Could not read output 0", __func__);
457 }
458
459 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
460 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
461
arovir01b0717b52018-09-05 17:03:25 +0100462 // ArmNN does not currently support non-fixed weights or bias
463
464 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
James Conroy6bf1cf02018-10-12 14:13:18 +0100465 // which is equal to [ M, H, W, I ]
arovir01b0717b52018-09-05 17:03:25 +0100466 const Operand* weightsOperand = GetInputOperand(operation, 1, model);
467
468 if (weightsOperand == nullptr)
469 {
470 return Fail("%s: Operand is invalid", __func__);
471 }
472
473 // Reinterpret weight data as [ H, W, I, M ]
474 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1], weightsOperand->dimensions[2],
475 inputInfo.GetShape()[3],
476 weightsOperand->dimensions[3] / inputInfo.GetShape()[3] });
477
James Conroy6bf1cf02018-10-12 14:13:18 +0100478 // Swizzle weight data [ H, W, I, M ] -> [ M, H, W, I ]
479 const armnn::PermutationVector HWIMToMHWI = { 1U, 2U, 3U, 0U };
480
arovir01b0717b52018-09-05 17:03:25 +0100481 ConstTensorPin weightsPin =
James Conroy6bf1cf02018-10-12 14:13:18 +0100482 ConvertOperationInputToConstTensorPin(operation, 1, model, data, HWIMToMHWI, &weightsShape);
arovir01b0717b52018-09-05 17:03:25 +0100483
484 // Bias is a 1D tensor
485 ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data);
486
487 if (!weightsPin.IsValid() || !biasPin.IsValid())
488 {
489 return Fail("%s: Operation has invalid inputs", __func__);
490 }
491
492 armnn::ConstTensor weights = weightsPin.GetConstTensor();
493 armnn::ConstTensor bias = biasPin.GetConstTensor();
James Conroy6bf1cf02018-10-12 14:13:18 +0100494 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
arovir01b0717b52018-09-05 17:03:25 +0100495
496 armnn::DepthwiseConvolution2dDescriptor desc;
James Conroy6bf1cf02018-10-12 14:13:18 +0100497 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +0100498 ActivationFn activation;
499
500 if (operation.inputs.size() == 11)
501 {
James Conroy6bf1cf02018-10-12 14:13:18 +0100502 if (!GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
503 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
504 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
505 !GetInputScalar(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
506 !GetInputScalar(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
507 !GetInputScalar(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
arovir01b0717b52018-09-05 17:03:25 +0100508 !GetInputActivationFunction(operation, 10, activation, model, data))
509 {
510 return Fail("%s: Operation has invalid inputs", __func__);
511 }
512 }
513 else if (operation.inputs.size() == 8)
514 {
515 android::nn::PaddingScheme paddingScheme;
James Conroy6bf1cf02018-10-12 14:13:18 +0100516 if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) ||
517 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
518 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
arovir01b0717b52018-09-05 17:03:25 +0100519 !GetInputActivationFunction(operation, 7, activation, model, data))
520 {
521 return Fail("%s: Operation has invalid inputs", __func__);
522 }
523
James Conroy6bf1cf02018-10-12 14:13:18 +0100524 const uint32_t kernelX = weights.GetShape()[2];
525 const uint32_t kernelY = weights.GetShape()[1];
526 const uint32_t inputX = inputInfo.GetShape()[2];
527 const uint32_t inputY = inputInfo.GetShape()[1];
arovir01b0717b52018-09-05 17:03:25 +0100528
529 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
530 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
531 }
532 else
533 {
534 return Fail("%s: Unsupported number of operation inputs", __func__);
535 }
536
537 desc.m_BiasEnabled = true;
arovir015602b192018-10-04 16:15:02 +0100538 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100539
540 if (!IsLayerSupported(__func__,
541 armnn::IsDepthwiseConvolutionSupported,
542 data.m_Compute,
James Conroy6bf1cf02018-10-12 14:13:18 +0100543 inputInfo,
544 outputInfo,
arovir01b0717b52018-09-05 17:03:25 +0100545 desc,
546 weights.GetInfo(),
547 biases))
548 {
549 return false;
550 }
551
552 armnn::IConnectableLayer* startLayer = data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, bias);
James Conroy6bf1cf02018-10-12 14:13:18 +0100553 if (!startLayer)
arovir01b0717b52018-09-05 17:03:25 +0100554 {
James Conroy6bf1cf02018-10-12 14:13:18 +0100555 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +0100556 }
James Conroy6bf1cf02018-10-12 14:13:18 +0100557
558 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
559 if (!endLayer)
arovir01b0717b52018-09-05 17:03:25 +0100560 {
561 return Fail("%s: ProcessActivation failed", __func__);
562 }
James Conroy6bf1cf02018-10-12 14:13:18 +0100563
564 input.Connect(startLayer->GetInputSlot(0));
565
566 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100567}
568
569bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
570{
571 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
572 if (!input.IsValid())
573 {
574 return Fail("%s: Operation has invalid inputs", __func__);
575 }
576
577 const Operand* const outputOperand = GetOutputOperand(operation, 0, model);
578 if (!outputOperand)
579 {
580 return Fail("%s: Operation has invalid outputs", __func__);
581 }
582
583 if (!IsLayerSupported(__func__,
584 armnn::IsFloorSupported,
585 data.m_Compute,
586 input.GetTensorInfo(),
587 GetTensorInfoForOperand(*outputOperand)))
588 {
589 return false;
590 }
591
592 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
593 assert(layer != nullptr);
594 input.Connect(layer->GetInputSlot(0));
595
596 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
597}
598
599bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
600{
601 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
602 if (!input.IsValid())
603 {
604 return Fail("%s: Operation has invalid inputs", __func__);
605 }
606
607 const Operand* output = GetOutputOperand(operation, 0, model);
608 if (!output)
609 {
610 return Fail("%s: Could not read output 0", __func__);
611 }
612
613 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
614 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
615
616 // ArmNN does not currently support non-fixed weights or bias
617 ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data); // 2D
618 ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); // 1D
619
620 if (!weightsPin.IsValid() || !biasPin.IsValid())
621 {
622 return Fail("%s: Operation has invalid inputs", __func__);
623 }
624
625 armnn::ConstTensor weights = weightsPin.GetConstTensor();
626 armnn::ConstTensor bias = biasPin.GetConstTensor();
627
628 armnn::TensorInfo reshapedInfo = inputInfo;
629 if (inputInfo.GetNumDimensions() > 2U)
630 {
631 unsigned int dim0 = inputInfo.GetShape()[0];
632 unsigned int dim1 = inputInfo.GetShape()[1];
633
634 for (unsigned int i = 2U; i < inputInfo.GetNumDimensions(); ++i)
635 {
636 dim1 *= inputInfo.GetShape()[i];
637 }
638
639 unsigned int divisor = weights.GetInfo().GetShape()[1] / dim1;
640 if(dim0 % divisor != 0)
641 {
642 return Fail("%s: Failed to deduce tensor shape", __func__);
643 }
644
645 reshapedInfo.SetShape(armnn::TensorShape({dim0 / divisor, dim1 * divisor}));
646 }
647
648 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
649 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
650
651 ActivationFn activationFunction;
652 if (!GetInputActivationFunction(operation, 3, activationFunction, model, data))
653 {
654 return Fail("%s: Operation has invalid inputs", __func__);
655 }
656
657 armnn::FullyConnectedDescriptor desc;
658 desc.m_TransposeWeightMatrix = true;
659 desc.m_BiasEnabled = true;
660
661 if (!IsLayerSupported(__func__,
662 armnn::IsFullyConnectedSupported,
663 data.m_Compute,
664 inputInfo,
665 outputInfo,
666 weights.GetInfo(),
667 bias.GetInfo(),
668 desc))
669 {
670 return false;
671 }
672
673 armnn::IConnectableLayer* startLayer = data.m_Network->AddFullyConnectedLayer(desc, weights, bias);
674 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
675
676 if (endLayer != nullptr)
677 {
678 if (inputInfo.GetNumDimensions() > 2U)
679 {
680 armnn::ReshapeDescriptor reshapeDescriptor;
681 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
682
683 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
684 assert(reshapeLayer != nullptr);
685 input.Connect(reshapeLayer->GetInputSlot(0));
686 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
687 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
688 }
689 else
690 {
691 input.Connect(startLayer->GetInputSlot(0));
692 }
693
694 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
695 }
696 else
697 {
698 return Fail("%s: ProcessActivation failed", __func__);
699 }
700}
701
702bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
703 const Model& model,
704 ConversionData& data)
705{
706 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
707 if (!input.IsValid())
708 {
709 return Fail("%s: Operation has invalid inputs", __func__);
710 }
711
712 const Operand* output = GetOutputOperand(operation, 0, model);
713 if (!output)
714 {
715 return Fail("%s: Could not read output 0", __func__);
716 }
717
narpra012fb804a2018-10-22 14:52:32 +0100718 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
arovir01b0717b52018-09-05 17:03:25 +0100719 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
720
arovir01b0717b52018-09-05 17:03:25 +0100721 armnn::NormalizationDescriptor descriptor;
722
narpra012fb804a2018-10-22 14:52:32 +0100723 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +0100724 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
narpra012fb804a2018-10-22 14:52:32 +0100725 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
arovir01b0717b52018-09-05 17:03:25 +0100726
727 if (!input.IsValid() ||
728 !GetInputScalar(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) ||
729 !GetInputFloat32(operation, 2, descriptor.m_K, model, data) ||
730 !GetInputFloat32(operation, 3, descriptor.m_Alpha, model, data) ||
731 !GetInputFloat32(operation, 4, descriptor.m_Beta, model, data))
732 {
733 return Fail("%s: Operation has invalid inputs", __func__);
734 }
735
736 // ArmNN expects normSize to be the full size of the normalization
737 // window rather than the radius as in AndroidNN.
738 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
739
740 if (!IsLayerSupported(__func__,
741 armnn::IsNormalizationSupported,
742 data.m_Compute,
narpra012fb804a2018-10-22 14:52:32 +0100743 inputInfo,
744 outputInfo,
arovir01b0717b52018-09-05 17:03:25 +0100745 descriptor))
746 {
747 return false;
748 }
749
750
751 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
752 assert(layer != nullptr);
narpra012fb804a2018-10-22 14:52:32 +0100753 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +0100754
narpra012fb804a2018-10-22 14:52:32 +0100755 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100756}
757
758bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
759{
760 armnn::ActivationDescriptor desc;
761 desc.m_Function = armnn::ActivationFunction::Sigmoid;
762
763 return ConvertToActivation(operation, __func__, desc, model, data);
764}
765
766bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
767{
768 // Inputs:
769 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
770 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
771 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
772 if (!input.IsValid())
773 {
774 return Fail("%s: Could not read input 0: input", __func__);
775 }
776 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
777 LayerInputHandle outputStateIn = ConvertToLayerInputHandle(operation, 18, model, data);
778 if (!outputStateIn.IsValid())
779 {
780 return Fail("%s: Could not read input 18: outputStateIn", __func__);
781 }
782 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
783 LayerInputHandle cellStateIn = ConvertToLayerInputHandle(operation, 19, model, data);
784 if (!cellStateIn.IsValid())
785 {
786 return Fail("%s: Could not read input 19: cellStateIn", __func__);
787 }
788
789 // Get the mandatory input tensors:
790 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
791 // [num_units, input_size].
792 const ConstTensorPin inputToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data);
793 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units, input_size].
794 const ConstTensorPin inputToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 3, model, data);
795 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
796 // [num_units, input_size].
797 const ConstTensorPin inputToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 4, model, data);
798 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
799 // [num_units, output_size].
800 const ConstTensorPin recurrentToForgetWeightsPin =
801 ConvertOperationInputToConstTensorPin(operation, 6, model, data);
802 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
803 // [num_units, output_size].
804 const ConstTensorPin recurrentToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 7, model, data);
805 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
806 // [num_units, output_size].
807 const ConstTensorPin recurrentToOutputWeightsPin =
808 ConvertOperationInputToConstTensorPin(operation, 8, model, data);
809 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
810 const ConstTensorPin forgetGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 13, model, data);
811 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
812 const ConstTensorPin cellBiasPin = ConvertOperationInputToConstTensorPin(operation, 14, model, data);
813 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
814 const ConstTensorPin outputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 15, model, data);
815
816 if (!inputToForgetWeightsPin.IsValid() ||
817 !inputToCellWeightsPin.IsValid() ||
818 !inputToOutputWeightsPin.IsValid() ||
819 !recurrentToForgetWeightsPin.IsValid() ||
820 !recurrentToCellWeightsPin.IsValid() ||
821 !recurrentToOutputWeightsPin.IsValid() ||
822 !forgetGateBiasPin.IsValid() ||
823 !cellBiasPin.IsValid() ||
824 !outputGateBiasPin.IsValid())
825 {
826 return Fail("%s: Operation has invalid tensor inputs", __func__);
827 }
828
829 // Get the optional input tensors:
830 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
831 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
832 const ConstTensorPin inputToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data);
833 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
834 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
835 // “num_units”), or the second dimension of the “projection_weights”, if defined.
836 const ConstTensorPin recurrentToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 5, model, data);
837 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
838 const ConstTensorPin cellToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 9, model, data);
839 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
840 const ConstTensorPin cellToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 10, model, data);
841 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
842 const ConstTensorPin cellToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 11, model, data);
843 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
844 const ConstTensorPin inputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 12, model, data);
845 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
846 // [output_size, num_units].
847 const ConstTensorPin projectionWeightsPin = ConvertOperationInputToConstTensorPin(operation, 16, model, data);
848 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
849 const ConstTensorPin projectionBiasPin = ConvertOperationInputToConstTensorPin(operation, 17, model, data);
850
851 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
852 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
853 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
854 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
855 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
856 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
857 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
858 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
859 {
860 return Fail("%s: Operation has invalid tensor inputs", __func__);
861 }
862
863 // Get the mandatory input scalars (actually 1-D tensors of size 1):
864 // 20: The activation function: A value indicating the activation function:
865 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
866 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
867 // If set to 0.0 then clipping is disabled.
868 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
869 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
870 ActivationFn activation;
871 float cellClip;
872 float projClip;
873 if (!GetInputActivationFunctionFromTensor(operation, 20, activation, model, data) ||
874 !GetInputScalar(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
875 !GetInputScalar(operation, 22, OperandType::FLOAT32, projClip, model, data))
876 {
877 return Fail("%s: Operation has invalid scalar inputs", __func__);
878 }
879
880 // Outputs:
881 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
882 // CIFG, or [batch_size, num_units * 3] without CIFG.
883 const Operand* scratchBuffer = GetOutputOperand(operation, 0, model);
884 if (!scratchBuffer)
885 {
886 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
887 }
888 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
889 const Operand* outputStateOut = GetOutputOperand(operation, 1, model);
890 if (!outputStateOut)
891 {
892 return Fail("%s: Could not read output 1: outputStateOut", __func__);
893 }
894 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
895 const Operand* cellStateOut = GetOutputOperand(operation, 2, model);
896 if (!cellStateOut)
897 {
898 return Fail("%s: Could not read output 2: cellStateOut", __func__);
899 }
900 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
901 // effectively the same as the current “output state (out)” value.
902 const Operand* output = GetOutputOperand(operation, 3, model);
903 if (!output)
904 {
905 return Fail("%s: Could not read output 3: output", __func__);
906 }
907
908 // set the params structure for the AddLstmLayer call
909 armnn::LstmInputParams params;
910 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
911 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
912 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
913 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
914 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
915 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
916 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
917 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
918 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
919 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
920 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
921 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
922 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
923 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
924 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
925 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
926 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
927
928 // set the layer descriptor
929 armnn::LstmDescriptor desc;
930 desc.m_ActivationFunc = activation;
931 desc.m_ClippingThresCell = cellClip;
932 desc.m_ClippingThresProj = projClip;
933 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
934 params.m_RecurrentToInputWeights == nullptr ||
935 params.m_InputGateBias == nullptr);
936 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
937 params.m_CellToOutputWeights != nullptr);
938 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
939
940 // validate the optional input groups
941 if (desc.m_CifgEnabled &&
942 (params.m_InputToInputWeights != nullptr ||
943 params.m_RecurrentToInputWeights != nullptr ||
944 params.m_InputGateBias != nullptr))
945 {
946 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
947 " and input gate bias must be provided", __func__);
948 }
949
950 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
951 {
952 return Fail("%s: projection bias should not be provided without projection weights", __func__);
953 }
954
955 if (desc.m_PeepholeEnabled &&
956 (params.m_CellToForgetWeights == nullptr ||
957 params.m_CellToOutputWeights == nullptr ||
958 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
959 {
960 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
961 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
962 }
963
964 // Check if the layer is supported
965 // Inputs
966 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
967 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
968 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
969
970 // Outputs
971 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
972 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
973 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
974 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
975
976 // Basic parameters
977 const armnn::TensorInfo& inputToForgetWeights = params.m_InputToForgetWeights->GetInfo();
978 const armnn::TensorInfo& inputToCellWeights = params.m_InputToCellWeights->GetInfo();
979 const armnn::TensorInfo& inputToOutputWeights = params.m_InputToOutputWeights->GetInfo();
980 const armnn::TensorInfo& recurrentToForgetWeights = params.m_RecurrentToForgetWeights->GetInfo();
981 const armnn::TensorInfo& recurrentToCellWeights = params.m_RecurrentToCellWeights->GetInfo();
982 const armnn::TensorInfo& recurrentToOutputWeights = params.m_RecurrentToOutputWeights->GetInfo();
983 const armnn::TensorInfo& forgetGateBias = params.m_ForgetGateBias->GetInfo();
984 const armnn::TensorInfo& cellBias = params.m_CellBias->GetInfo();
985 const armnn::TensorInfo& outputGateBias = params.m_OutputGateBias->GetInfo();
986
987 //Optional parameters
988 const armnn::TensorInfo* inputToInputWeights = nullptr;
989 const armnn::TensorInfo* recurrentToInputWeights = nullptr;
990 const armnn::TensorInfo* cellToInputWeights = nullptr;
991 const armnn::TensorInfo* inputGateBias = nullptr;
992 const armnn::TensorInfo* projectionWeights = nullptr;
993 const armnn::TensorInfo* projectionBias = nullptr;
994 const armnn::TensorInfo* cellToForgetWeights = nullptr;
995 const armnn::TensorInfo* cellToOutputWeights = nullptr;
996
997 if(!desc.m_CifgEnabled)
998 {
999 inputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1000 recurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1001 if (params.m_CellToInputWeights != nullptr)
1002 {
1003 cellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
1004 }
1005 inputGateBias = &(params.m_InputGateBias->GetInfo());
1006 }
1007
1008 if(desc.m_ProjectionEnabled)
1009 {
1010 projectionWeights = &(params.m_ProjectionWeights->GetInfo());
1011 if (params.m_ProjectionBias != nullptr)
1012 {
1013 projectionBias = &(params.m_ProjectionBias->GetInfo());
1014 }
1015 }
1016
1017 if(desc.m_PeepholeEnabled)
1018 {
1019 cellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
1020 cellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
1021 }
1022
1023 if (!IsLayerSupported(__func__,
1024 armnn::IsLstmSupported,
1025 data.m_Compute,
1026 inputInfo,
1027 outputStateInInfo,
1028 cellStateInInfo,
1029 scratchBufferInfo,
1030 outputStateOutInfo,
1031 cellStateOutInfo,
1032 outputInfo,
1033 desc,
1034 inputToForgetWeights,
1035 inputToCellWeights,
1036 inputToOutputWeights,
1037 recurrentToForgetWeights,
1038 recurrentToCellWeights,
1039 recurrentToOutputWeights,
1040 forgetGateBias,
1041 cellBias,
1042 outputGateBias,
1043 inputToInputWeights,
1044 recurrentToInputWeights,
1045 cellToInputWeights,
1046 inputGateBias,
1047 projectionWeights,
1048 projectionBias,
1049 cellToForgetWeights,
1050 cellToOutputWeights))
1051 {
1052 return false;
1053 }
1054
1055 // Add the layer
1056 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
1057
1058 input.Connect(layer->GetInputSlot(0));
1059 outputStateIn.Connect(layer->GetInputSlot(1));
1060 cellStateIn.Connect(layer->GetInputSlot(2));
1061
1062 return (SetupAndTrackLayerOutputSlot(operation, 0, *layer, 0, model, data) &&
1063 SetupAndTrackLayerOutputSlot(operation, 1, *layer, 1, model, data) &&
1064 SetupAndTrackLayerOutputSlot(operation, 2, *layer, 2, model, data) &&
1065 SetupAndTrackLayerOutputSlot(operation, 3, *layer, 3, model, data));
1066}
1067
1068bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1069{
1070 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1071 if (!input.IsValid())
1072 {
1073 return Fail("%s: Operation has invalid inputs", __func__);
1074 }
1075
1076 const Operand* output = GetOutputOperand(operation, 0, model);
1077 if (!output)
1078 {
1079 return Fail("%s: Could not read output 0", __func__);
1080 }
1081
1082 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1083 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1084
Matteo Martincigh58f71092018-09-25 15:58:52 +01001085 armnn::L2NormalizationDescriptor desc;
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +01001086 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matteo Martincigh58f71092018-09-25 15:58:52 +01001087
arovir01b0717b52018-09-05 17:03:25 +01001088 if (!IsLayerSupported(__func__,
1089 armnn::IsL2NormalizationSupported,
1090 data.m_Compute,
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +01001091 inputInfo,
1092 outputInfo,
Matteo Martincigh58f71092018-09-25 15:58:52 +01001093 desc))
arovir01b0717b52018-09-05 17:03:25 +01001094 {
1095 return false;
1096 }
1097
Matteo Martincigh58f71092018-09-25 15:58:52 +01001098 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer(desc);
arovir01b0717b52018-09-05 17:03:25 +01001099 assert(layer != nullptr);
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +01001100 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +01001101
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +01001102 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001103}
1104
1105bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1106{
1107 return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
1108}
1109
1110bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1111{
1112 return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
1113}
1114
1115bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1116{
1117 LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data);
1118 LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data);
1119
1120 if (!input0.IsValid() || !input1.IsValid())
1121 {
1122 return Fail("%s: Operation has invalid inputs", __func__);
1123 }
1124
1125 // The FuseActivation parameter is always the input index 2
1126 // and it should be optional
1127 ActivationFn activationFunction;
1128 if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data))
1129 {
1130 return Fail("%s: Operation has invalid inputs", __func__);
1131 }
1132
1133 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
1134
1135 if (outputOperand == nullptr)
1136 {
1137 return false;
1138 }
1139
1140 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
1141
1142 if (!IsLayerSupported(__func__,
1143 armnn::IsMultiplicationSupported,
1144 data.m_Compute,
1145 input0.GetTensorInfo(),
1146 input1.GetTensorInfo(),
1147 outInfo))
1148 {
1149 return false;
1150 }
1151
1152 armnn::IConnectableLayer* const startLayer = data.m_Network->AddMultiplicationLayer();
1153 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
1154
1155 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
1156 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
1157
1158 if (endLayer != nullptr)
1159 {
1160 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
1161 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
1162 }
1163 else
1164 {
1165 return Fail("%s: ProcessActivation failed", __func__);
1166 }
1167}
1168
1169bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1170{
1171 armnn::ActivationDescriptor desc;
1172 desc.m_Function = armnn::ActivationFunction::ReLu;
1173
1174 return ConvertToActivation(operation, __func__, desc, model, data);
1175}
1176
1177bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1178{
1179 armnn::ActivationDescriptor desc;
1180 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1181 desc.m_A = 1.0f;
1182 desc.m_B = -1.0f;
1183
1184 return ConvertToActivation(operation, __func__, desc, model, data);
1185}
1186
1187bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1188{
1189 armnn::ActivationDescriptor desc;
1190 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1191 desc.m_A = 6.0f;
1192
1193 return ConvertToActivation(operation, __func__, desc, model, data);
1194}
1195
1196bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1197{
1198 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1199 if (!input.IsValid())
1200 {
1201 return Fail("%s: Operation has invalid inputs", __func__);
1202 }
1203
1204 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
1205 if (!outputOperand)
1206 {
1207 return Fail("%s: Operation has no outputs", __func__);
1208 }
1209
1210 const armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
1211
1212 armnn::SoftmaxDescriptor desc;
1213 if (!GetInputFloat32(operation, 1, desc.m_Beta, model, data))
1214 {
1215 return Fail("%s: Operation has invalid inputs", __func__);
1216 }
1217
1218 if (!IsLayerSupported(__func__,
1219 armnn::IsSoftmaxSupported,
1220 data.m_Compute,
1221 input.GetTensorInfo(),
1222 outInfo,
1223 desc))
1224 {
1225 return false;
1226 }
1227
1228 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1229 assert(layer != nullptr);
1230 input.Connect(layer->GetInputSlot(0));
1231
1232 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
1233}
1234
1235bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1236{
1237 armnn::ActivationDescriptor desc;
1238 desc.m_Function = armnn::ActivationFunction::TanH;
1239 desc.m_A = 1.0f; // android nn does not support tanH parameters
1240 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1241
1242 return ConvertToActivation(operation, __func__, desc, model, data);
1243}
1244
1245bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1246{
1247 const Operand* inputOperand = GetInputOperand(operation, 0, model);
1248 const Operand* requestedShapeOperand = GetInputOperand(operation, 1, model);
1249 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
1250
1251 if (inputOperand == nullptr
1252 || requestedShapeOperand == nullptr
1253 || outputOperand == nullptr)
1254 {
1255 return Fail("%s: Operation has invalid inputs", __func__);
1256 }
1257
1258
1259 if (requestedShapeOperand->dimensions.size() != 1)
1260 {
1261 return Fail("%s: Input 1 expected to be one-dimensional (found %i dimensions)",
1262 __func__, requestedShapeOperand->dimensions.size());
1263 }
1264
1265 std::vector<int32_t> targetDimensions;
1266 if (!GetTensorInt32Values(*requestedShapeOperand, targetDimensions, model, data))
1267 {
1268 return Fail("%s: Could not read values of input 1", __func__);
1269 }
1270
1271 const Shape inputOperandShape = GetOperandShape(*inputOperand);
1272
1273 Shape requestedShape;
1274 // targetDimensions may contain special values (e.g. -1). reshapePrepare() is an AndroidNN provided utility
1275 // function that resolves these values into a fully specified tensor shape.
1276 if (!reshapePrepare(inputOperandShape, targetDimensions.data(), targetDimensions.size(), &requestedShape))
1277 {
1278 return Fail("%s: Failed to resolve the requested shape", __func__);
1279 }
1280
1281 const Shape outputOperandShape = GetOperandShape(*outputOperand);
1282 if (!SameShape(requestedShape, outputOperandShape))
1283 {
1284 return Fail("%s: Shape of output operand does not match resolved requested shape", __func__);
1285 }
1286
1287 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1288 if (!input.IsValid())
1289 {
1290 return Fail("%s: Could not read input 0", __func__);
1291 }
1292
1293 if (!IsLayerSupported(__func__,
1294 armnn::IsReshapeSupported,
1295 data.m_Compute,
1296 input.GetTensorInfo()))
1297 {
1298 return false;
1299 }
1300
1301
1302 armnn::ReshapeDescriptor reshapeDescriptor;
1303 reshapeDescriptor.m_TargetShape = armnn::TensorShape(requestedShape.dimensions.size(),
1304 requestedShape.dimensions.data());
1305
1306 armnn::IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
1307 assert(layer != nullptr);
1308 input.Connect(layer->GetInputSlot(0));
1309
1310 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
1311}
1312
1313bool HalPolicy::ConvertResizeBilinear(const Operation& operation, const Model& model, ConversionData& data)
1314{
1315 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1316 if (!input.IsValid())
1317 {
1318 return Fail("%s: Could not read input 0", __func__);
1319 }
1320
1321 const Operand* output = GetOutputOperand(operation, 0, model);
1322 if (!output)
1323 {
1324 return Fail("%s: Could not read output 0", __func__);
1325 }
1326
1327 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1328 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1329
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001330 armnn::ResizeBilinearDescriptor desc;
1331 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001332
1333 if (!IsLayerSupported(__func__,
1334 armnn::IsResizeBilinearSupported,
1335 data.m_Compute,
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001336 inputInfo))
arovir01b0717b52018-09-05 17:03:25 +01001337 {
1338 return false;
1339 }
1340
arovir01b0717b52018-09-05 17:03:25 +01001341
1342 if ( !GetInputScalar(operation, 1, OperandType::INT32, desc.m_TargetHeight, model, data)
1343 || !GetInputScalar(operation, 2, OperandType::INT32, desc.m_TargetWidth, model, data))
1344 {
1345 return Fail("%s: Operation has invalid inputs", __func__);
1346 }
1347
1348 armnn::IConnectableLayer* layer = data.m_Network->AddResizeBilinearLayer(desc);
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001349
arovir01b0717b52018-09-05 17:03:25 +01001350 assert(layer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +01001351
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001352 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1353 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +01001354
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001355 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001356
1357}
1358
1359} // namespace hal_1_0
Matteo Martincigh58f71092018-09-25 15:58:52 +01001360} // namespace armnn_driver