blob: d3c6dba141cfd42af3e871f98fdb4fc7f74a7db1 [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
8namespace armnn_driver
9{
10namespace hal_1_0
11{
12
13bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
14{
15 switch (operation.type)
16 {
17 case V1_0::OperationType::ADD:
18 return ConvertAdd(operation, model, data);
19 case V1_0::OperationType::AVERAGE_POOL_2D:
20 return ConvertAveragePool2d(operation, model, data);
21 case V1_0::OperationType::CONCATENATION:
22 return ConvertConcatenation(operation, model, data);
23 case V1_0::OperationType::CONV_2D:
24 return ConvertConv2d(operation, model, data);
25 case V1_0::OperationType::DEPTHWISE_CONV_2D:
26 return ConvertDepthwiseConv2d(operation, model, data);
27 case V1_0::OperationType::FLOOR:
28 return ConvertFloor(operation, model, data);
29 case V1_0::OperationType::FULLY_CONNECTED:
30 return ConvertFullyConnected(operation, model, data);
31 case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
32 return ConvertLocalResponseNormalization(operation, model, data);
33 case V1_0::OperationType::LOGISTIC:
34 return ConvertLogistic(operation, model, data);
35 case V1_0::OperationType::LSTM:
36 return ConvertLstm(operation, model, data);
37 case V1_0::OperationType::L2_NORMALIZATION:
38 return ConvertL2Normalization(operation, model, data);
39 case V1_0::OperationType::L2_POOL_2D:
40 return ConvertL2Pool2d(operation, model, data);
41 case V1_0::OperationType::MAX_POOL_2D:
42 return ConvertMaxPool2d(operation, model, data);
43 case V1_0::OperationType::MUL:
44 return ConvertMul(operation, model, data);
45 case V1_0::OperationType::RELU:
46 return ConvertReLu(operation, model, data);
47 case V1_0::OperationType::RELU1:
48 return ConvertReLu1(operation, model, data);
49 case V1_0::OperationType::RELU6:
50 return ConvertReLu6(operation, model, data);
51 case V1_0::OperationType::SOFTMAX:
52 return ConvertSoftmax(operation, model, data);
53 case V1_0::OperationType::TANH:
54 return ConvertTanH(operation, model, data);
55 case V1_0::OperationType::RESHAPE:
56 return ConvertReshape(operation, model, data);
57 case V1_0::OperationType::RESIZE_BILINEAR:
58 return ConvertResizeBilinear(operation, model, data);
59 default:
60 return Fail("%s: Operation type %s not supported in ArmnnDriver",
61 __func__, toString(operation.type).c_str());
62 }
63}
64
65bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
66{
67 LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data);
68 LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data);
69
70 if (!input0.IsValid() || !input1.IsValid())
71 {
72 return Fail("%s: Operation has invalid inputs", __func__);
73 }
74
75 // The FuseActivation parameter is always the input index 2
76 // and it should be optional
77 ActivationFn activationFunction;
78 if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data))
79 {
80 return Fail("%s: Operation has invalid inputs", __func__);
81 }
82
83 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
84 if (!outputOperand)
85 {
86 return false;
87 }
88
89 const armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
90
91 if (!IsLayerSupported(__func__,
92 armnn::IsAdditionSupported,
93 data.m_Compute,
94 input0.GetTensorInfo(),
95 input1.GetTensorInfo(),
96 outInfo))
97 {
98 return false;
99 }
100
101 armnn::IConnectableLayer* const startLayer = data.m_Network->AddAdditionLayer();
102 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
103
104 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
105 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
106
107 if (endLayer != nullptr)
108 {
109 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
110 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
111 }
112 else
113 {
114 return Fail("%s: ProcessActivation failed", __func__);
115 }
116}
117
118bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
119{
120 return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::Average, model, data);
121}
122
123bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
124{
125 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
126 if (operation.inputs.size() <= 1)
127 {
128 return Fail("%s: Operation has insufficient arguments", __func__);
129 }
130
131 // Get inputs and outputs
132 const std::size_t numInputTensors = operation.inputs.size() - 1;
133
134 int32_t concatDim;
135 if (!GetInputScalar(operation, numInputTensors, OperandType::INT32, concatDim, model, data))
136 {
137 return Fail("%s: Operation has invalid inputs", __func__);
138 }
139
140 const Operand* const outputOperand = GetOutputOperand(operation, 0, model);
141 if (!outputOperand)
142 {
143 return Fail("%s: Operation has no outputs", __func__);
144 }
145
146
147 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
148 armnn::TensorShape outputShape = outputInfo.GetShape();
149
150 //
151 // handle negative concat dims along the lines of tensorflow as described here:
152 // https://www.tensorflow.org/api_docs/python/tf/concat
153 // "negative axis refers to axis + rank(values)-th dimension"
154 //
155 if (concatDim < 0)
156 {
157 concatDim += outputShape.GetNumDimensions();
158 }
159
160 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
161 {
162 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
163 }
164
165 std::vector<LayerInputHandle> inputHandles;
166 std::vector<armnn::TensorShape> inputShapes;
167
168 inputHandles.reserve(numInputTensors);
169 inputShapes.reserve(numInputTensors);
170
171 bool inputsHaveBeenReshaped = false;
172 unsigned int tensorDimensionsAdded = 0;
173
174 for (uint32_t i = 0; i < numInputTensors; ++i)
175 {
176 const Operand* const operand = GetInputOperand(operation, i, model);
177 if (!operand)
178 {
179 return Fail("%s: Operation has invalid inputs", __func__);
180 }
181
182 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
183 LayerInputHandle operandInputHandle = ConvertToLayerInputHandle(operation, i, model, data);
184
185 if (operandShape.GetNumDimensions() == 0)
186 {
187 return Fail("%s: Operands with rank 0 are not supported", __func__);
188 }
189
190 if (RequiresReshape(operandShape))
191 {
192 inputsHaveBeenReshaped = true;
193
194 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
195
196 // Expand the tensor to three dimensions
197 if (operandShape.GetNumDimensions() == 2)
198 {
199 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
200 tensorDimensionsAdded = 1;
201 }
202 else
203 {
204 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
205 tensorDimensionsAdded = 2;
206 }
207
208 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
209 *data.m_Network,
210 operandInputHandle,
211 reshapeInfo
212 );
213
214 // Point to the reshape operation rather then the input operation
215 operandShape = reshapeInfo.GetShape();
216 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
217 }
218
219 inputShapes.emplace_back(operandShape);
220 inputHandles.emplace_back(operandInputHandle);
221
222 if (!inputHandles.back().IsValid())
223 {
224 return Fail("%s: Operation has invalid inputs", __func__);
225 }
226 }
227
228 BOOST_ASSERT(inputShapes.size() == inputHandles.size());
229
230 if (inputsHaveBeenReshaped)
231 {
232 // Adjust the concatenation dimension by the amount of dimensions added (if any)
233 concatDim += tensorDimensionsAdded;
234
235 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
236 if (tensorDimensionsAdded == 1)
237 {
238 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
239 }
240 else if (tensorDimensionsAdded == 2)
241 {
242 outputShape = armnn::TensorShape({1, 1, outputShape[0], outputShape[1]});
243 }
244 }
245
246 // Get the pair of permutations required for the concatenation
247 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
248 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
249
250 CreatePermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
251
252 outputShape = armnnUtils::Permuted(outputShape, permutationPair.first);
253 outputInfo.SetShape(outputShape);
254
255 // this is no-op for identity swizzles, otherwise it replaces both
256 // the handles and shapes with the swizzled layer output handles and shapes
257 SwizzleInputs(*data.m_Network, inputHandles, inputShapes, permutationPair.first);
258
259 // Create an armnn merger layer descriptor - this will also perform validation on the input shapes
260 armnn::OriginsDescriptor mergerDescriptor;
261 try
262 {
263 // The merger descriptor is always created across the only supported concat
264 // dimension, which is 0 or 1
265 mergerDescriptor =
266 armnn::CreateMergerDescriptorForConcatenation(
267 inputShapes.begin(), inputShapes.end(), concatDim);
268 }
269 catch (const armnn::Exception& error)
270 {
271 return Fail("%s: Error preparing merger descriptor. %s", __func__, error.what());
272 }
273
274 // Validate the output shape is correct given the input shapes based on the
275 // only valid concat dimension which is 0 or 1
276 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
277 {
278 return Fail("%s: Error validating the output shape for concat", __func__);
279 }
280
281 std::vector<const armnn::TensorInfo*> inputTensorInfos;
282 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
283 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
284 if (!IsLayerSupported(__func__,
285 armnn::IsMergerSupported,
286 data.m_Compute,
287 inputTensorInfos,
288 mergerDescriptor))
289 {
290 return false;
291 }
292
293 armnn::IConnectableLayer* layer = data.m_Network->AddMergerLayer(mergerDescriptor);
294 assert(layer != nullptr);
295 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
296
297 // Connect inputs to the layer
298 const int numInputSlots = layer->GetNumInputSlots();
299 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
300 for (int i = 0; i < numInputSlots; ++i)
301 {
302 // connect the input directly to the merge (concat) layer
303 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
304 }
305
306 // Add permutation layer and connect the output to it, the permutation becomes the output layer
307 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(*data.m_Network,
308 layer->GetOutputSlot(0),
309 permutationPair.second);
310 layer = &deswizzleLayer;
311
312 if (inputsHaveBeenReshaped)
313 {
314 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
315
316 // Undo the reshape knowing the amount of dimensions added
317 if (tensorDimensionsAdded == 1)
318 {
319 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
320 afterConcatInfo.GetShape()[2] }));
321 }
322 else if (tensorDimensionsAdded == 2)
323 {
324 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2],
325 afterConcatInfo.GetShape()[3] }));
326 }
327
328 layer = &AddReshapeLayer(
329 *data.m_Network,
330 layer->GetOutputSlot(0),
331 afterConcatInfo
332 );
333 }
334
335 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
336}
337
338bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data)
339{
340 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
341 if (!input.IsValid())
342 {
343 return Fail("%s: Operation has invalid inputs", __func__);
344 }
345
346 const Operand* output = GetOutputOperand(operation, 0, model);
347 if (!output)
348 {
349 return Fail("%s: Could not read output 0", __func__);
350 }
351
352 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
353 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
354
355 const armnn::TensorInfo swizzledInputInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
356 const armnn::TensorInfo swizzledOutputInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
357
358 // ArmNN does not currently support non-fixed weights or bias
359 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data, NHWCToArmNN);
360 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data);
361
362 if (!weightsPin.IsValid() || !biasPin.IsValid())
363 {
364 return Fail("%s: Operation has invalid inputs", __func__);
365 }
366
367 armnn::ConstTensor weights = weightsPin.GetConstTensor();
368 armnn::ConstTensor bias = biasPin.GetConstTensor();
369 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), swizzledInputInfo);
370
371 armnn::Convolution2dDescriptor desc;
372 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
398 const uint32_t kernelX = weights.GetShape()[3];
399 const uint32_t kernelY = weights.GetShape()[2];
400 const uint32_t inputX = swizzledInputInfo.GetShape()[3];
401 const uint32_t inputY = swizzledInputInfo.GetShape()[2];
402
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;
412 auto biases = boost::make_optional(bias.GetInfo());
413
414 if (!IsLayerSupported(__func__,
415 armnn::IsConvolution2dSupported,
416 data.m_Compute,
417 swizzledInputInfo,
418 swizzledOutputInfo,
419 desc,
420 weights.GetInfo(),
421 biases))
422 {
423 return false;
424 }
425
426 armnn::IConnectableLayer* startLayer = data.m_Network->AddConvolution2dLayer(desc, weights, bias);
427 armnn::IConnectableLayer* endLayer = ProcessActivation(swizzledOutputInfo, activation, startLayer, data);
428
429 if (endLayer != nullptr)
430 {
431 armnn::IConnectableLayer& outSwizzleLayer =
432 SwizzleInDeswizzleOut(*data.m_Network, input, *startLayer, *endLayer);
433 return SetupAndTrackLayerOutputSlot(operation, 0, outSwizzleLayer, model, data);
434 }
435 else
436 {
437 return Fail("%s: ProcessActivation failed", __func__);
438 }
439}
440
441bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
442{
443 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
444 if (!input.IsValid())
445 {
446 return Fail("%s: Operation has invalid inputs", __func__);
447 }
448
449 const Operand* output = GetOutputOperand(operation, 0, model);
450 if (!output)
451 {
452 return Fail("%s: Could not read output 0", __func__);
453 }
454
455 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
456 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
457
458 const armnn::TensorInfo swizzledInputInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
459 const armnn::TensorInfo swizzledOutputInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
460
461 // ArmNN does not currently support non-fixed weights or bias
462
463 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
464 // but in ArmNN it needs to be [ M, I, H, W ]
465 const Operand* weightsOperand = GetInputOperand(operation, 1, model);
466
467 if (weightsOperand == nullptr)
468 {
469 return Fail("%s: Operand is invalid", __func__);
470 }
471
472 // Reinterpret weight data as [ H, W, I, M ]
473 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1], weightsOperand->dimensions[2],
474 inputInfo.GetShape()[3],
475 weightsOperand->dimensions[3] / inputInfo.GetShape()[3] });
476
477 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
478 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
479 ConstTensorPin weightsPin =
480 ConvertOperationInputToConstTensorPin(operation, 1, model, data, HWIMToMIHW, &weightsShape);
481
482 // Bias is a 1D tensor
483 ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data);
484
485 if (!weightsPin.IsValid() || !biasPin.IsValid())
486 {
487 return Fail("%s: Operation has invalid inputs", __func__);
488 }
489
490 armnn::ConstTensor weights = weightsPin.GetConstTensor();
491 armnn::ConstTensor bias = biasPin.GetConstTensor();
492 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), swizzledInputInfo);
493
494 armnn::DepthwiseConvolution2dDescriptor desc;
495 ActivationFn activation;
496
497 if (operation.inputs.size() == 11)
498 {
499 if (!GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
500 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
501 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
502 !GetInputScalar(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
503 !GetInputScalar(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
504 !GetInputScalar(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
505 !GetInputActivationFunction(operation, 10, activation, model, data))
506 {
507 return Fail("%s: Operation has invalid inputs", __func__);
508 }
509 }
510 else if (operation.inputs.size() == 8)
511 {
512 android::nn::PaddingScheme paddingScheme;
513 if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) ||
514 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
515 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
516 !GetInputActivationFunction(operation, 7, activation, model, data))
517 {
518 return Fail("%s: Operation has invalid inputs", __func__);
519 }
520
521 const uint32_t kernelX = weights.GetShape()[3];
522 const uint32_t kernelY = weights.GetShape()[2];
523 const uint32_t inputX = swizzledInputInfo.GetShape()[3];
524 const uint32_t inputY = swizzledInputInfo.GetShape()[2];
525
526 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
527 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
528 }
529 else
530 {
531 return Fail("%s: Unsupported number of operation inputs", __func__);
532 }
533
534 desc.m_BiasEnabled = true;
535 auto biases = boost::make_optional(bias.GetInfo());
536
537 if (!IsLayerSupported(__func__,
538 armnn::IsDepthwiseConvolutionSupported,
539 data.m_Compute,
540 swizzledInputInfo,
541 swizzledOutputInfo,
542 desc,
543 weights.GetInfo(),
544 biases))
545 {
546 return false;
547 }
548
549 armnn::IConnectableLayer* startLayer = data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, bias);
550 armnn::IConnectableLayer* endLayer = ProcessActivation(swizzledOutputInfo, activation, startLayer, data);
551
552 if (endLayer != nullptr)
553 {
554 armnn::IConnectableLayer& outSwizzleLayer =
555 SwizzleInDeswizzleOut(*data.m_Network, input, *startLayer, *endLayer);
556 return SetupAndTrackLayerOutputSlot(operation, 0, outSwizzleLayer, model, data);
557 }
558 else
559 {
560 return Fail("%s: ProcessActivation failed", __func__);
561 }
562}
563
564bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
565{
566 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
567 if (!input.IsValid())
568 {
569 return Fail("%s: Operation has invalid inputs", __func__);
570 }
571
572 const Operand* const outputOperand = GetOutputOperand(operation, 0, model);
573 if (!outputOperand)
574 {
575 return Fail("%s: Operation has invalid outputs", __func__);
576 }
577
578 if (!IsLayerSupported(__func__,
579 armnn::IsFloorSupported,
580 data.m_Compute,
581 input.GetTensorInfo(),
582 GetTensorInfoForOperand(*outputOperand)))
583 {
584 return false;
585 }
586
587 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
588 assert(layer != nullptr);
589 input.Connect(layer->GetInputSlot(0));
590
591 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
592}
593
594bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
595{
596 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
597 if (!input.IsValid())
598 {
599 return Fail("%s: Operation has invalid inputs", __func__);
600 }
601
602 const Operand* output = GetOutputOperand(operation, 0, model);
603 if (!output)
604 {
605 return Fail("%s: Could not read output 0", __func__);
606 }
607
608 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
609 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
610
611 // ArmNN does not currently support non-fixed weights or bias
612 ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data); // 2D
613 ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); // 1D
614
615 if (!weightsPin.IsValid() || !biasPin.IsValid())
616 {
617 return Fail("%s: Operation has invalid inputs", __func__);
618 }
619
620 armnn::ConstTensor weights = weightsPin.GetConstTensor();
621 armnn::ConstTensor bias = biasPin.GetConstTensor();
622
623 armnn::TensorInfo reshapedInfo = inputInfo;
624 if (inputInfo.GetNumDimensions() > 2U)
625 {
626 unsigned int dim0 = inputInfo.GetShape()[0];
627 unsigned int dim1 = inputInfo.GetShape()[1];
628
629 for (unsigned int i = 2U; i < inputInfo.GetNumDimensions(); ++i)
630 {
631 dim1 *= inputInfo.GetShape()[i];
632 }
633
634 unsigned int divisor = weights.GetInfo().GetShape()[1] / dim1;
635 if(dim0 % divisor != 0)
636 {
637 return Fail("%s: Failed to deduce tensor shape", __func__);
638 }
639
640 reshapedInfo.SetShape(armnn::TensorShape({dim0 / divisor, dim1 * divisor}));
641 }
642
643 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
644 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
645
646 ActivationFn activationFunction;
647 if (!GetInputActivationFunction(operation, 3, activationFunction, model, data))
648 {
649 return Fail("%s: Operation has invalid inputs", __func__);
650 }
651
652 armnn::FullyConnectedDescriptor desc;
653 desc.m_TransposeWeightMatrix = true;
654 desc.m_BiasEnabled = true;
655
656 if (!IsLayerSupported(__func__,
657 armnn::IsFullyConnectedSupported,
658 data.m_Compute,
659 inputInfo,
660 outputInfo,
661 weights.GetInfo(),
662 bias.GetInfo(),
663 desc))
664 {
665 return false;
666 }
667
668 armnn::IConnectableLayer* startLayer = data.m_Network->AddFullyConnectedLayer(desc, weights, bias);
669 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
670
671 if (endLayer != nullptr)
672 {
673 if (inputInfo.GetNumDimensions() > 2U)
674 {
675 armnn::ReshapeDescriptor reshapeDescriptor;
676 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
677
678 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
679 assert(reshapeLayer != nullptr);
680 input.Connect(reshapeLayer->GetInputSlot(0));
681 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
682 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
683 }
684 else
685 {
686 input.Connect(startLayer->GetInputSlot(0));
687 }
688
689 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
690 }
691 else
692 {
693 return Fail("%s: ProcessActivation failed", __func__);
694 }
695}
696
697bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
698 const Model& model,
699 ConversionData& data)
700{
701 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
702 if (!input.IsValid())
703 {
704 return Fail("%s: Operation has invalid inputs", __func__);
705 }
706
707 const Operand* output = GetOutputOperand(operation, 0, model);
708 if (!output)
709 {
710 return Fail("%s: Could not read output 0", __func__);
711 }
712
713 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
714 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
715
716 const armnn::TensorInfo swizzledInputInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
717 const armnn::TensorInfo swizzledOutputInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
718
719 armnn::NormalizationDescriptor descriptor;
720
721 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
722 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
723
724 if (!input.IsValid() ||
725 !GetInputScalar(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) ||
726 !GetInputFloat32(operation, 2, descriptor.m_K, model, data) ||
727 !GetInputFloat32(operation, 3, descriptor.m_Alpha, model, data) ||
728 !GetInputFloat32(operation, 4, descriptor.m_Beta, model, data))
729 {
730 return Fail("%s: Operation has invalid inputs", __func__);
731 }
732
733 // ArmNN expects normSize to be the full size of the normalization
734 // window rather than the radius as in AndroidNN.
735 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
736
737 if (!IsLayerSupported(__func__,
738 armnn::IsNormalizationSupported,
739 data.m_Compute,
740 swizzledInputInfo,
741 swizzledOutputInfo,
742 descriptor))
743 {
744 return false;
745 }
746
747
748 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
749 assert(layer != nullptr);
750 layer->GetOutputSlot(0).SetTensorInfo(swizzledOutputInfo);
751
752 armnn::IConnectableLayer& outSwizzleLayer = SwizzleInDeswizzleOut(*data.m_Network, input, *layer);
753
754 return SetupAndTrackLayerOutputSlot(operation, 0, outSwizzleLayer, model, data);
755}
756
757bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
758{
759 armnn::ActivationDescriptor desc;
760 desc.m_Function = armnn::ActivationFunction::Sigmoid;
761
762 return ConvertToActivation(operation, __func__, desc, model, data);
763}
764
765bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
766{
767 // Inputs:
768 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
769 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
770 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
771 if (!input.IsValid())
772 {
773 return Fail("%s: Could not read input 0: input", __func__);
774 }
775 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
776 LayerInputHandle outputStateIn = ConvertToLayerInputHandle(operation, 18, model, data);
777 if (!outputStateIn.IsValid())
778 {
779 return Fail("%s: Could not read input 18: outputStateIn", __func__);
780 }
781 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
782 LayerInputHandle cellStateIn = ConvertToLayerInputHandle(operation, 19, model, data);
783 if (!cellStateIn.IsValid())
784 {
785 return Fail("%s: Could not read input 19: cellStateIn", __func__);
786 }
787
788 // Get the mandatory input tensors:
789 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
790 // [num_units, input_size].
791 const ConstTensorPin inputToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data);
792 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units, input_size].
793 const ConstTensorPin inputToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 3, model, data);
794 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
795 // [num_units, input_size].
796 const ConstTensorPin inputToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 4, model, data);
797 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
798 // [num_units, output_size].
799 const ConstTensorPin recurrentToForgetWeightsPin =
800 ConvertOperationInputToConstTensorPin(operation, 6, model, data);
801 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
802 // [num_units, output_size].
803 const ConstTensorPin recurrentToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 7, model, data);
804 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
805 // [num_units, output_size].
806 const ConstTensorPin recurrentToOutputWeightsPin =
807 ConvertOperationInputToConstTensorPin(operation, 8, model, data);
808 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
809 const ConstTensorPin forgetGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 13, model, data);
810 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
811 const ConstTensorPin cellBiasPin = ConvertOperationInputToConstTensorPin(operation, 14, model, data);
812 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
813 const ConstTensorPin outputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 15, model, data);
814
815 if (!inputToForgetWeightsPin.IsValid() ||
816 !inputToCellWeightsPin.IsValid() ||
817 !inputToOutputWeightsPin.IsValid() ||
818 !recurrentToForgetWeightsPin.IsValid() ||
819 !recurrentToCellWeightsPin.IsValid() ||
820 !recurrentToOutputWeightsPin.IsValid() ||
821 !forgetGateBiasPin.IsValid() ||
822 !cellBiasPin.IsValid() ||
823 !outputGateBiasPin.IsValid())
824 {
825 return Fail("%s: Operation has invalid tensor inputs", __func__);
826 }
827
828 // Get the optional input tensors:
829 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
830 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
831 const ConstTensorPin inputToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data);
832 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
833 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
834 // “num_units”), or the second dimension of the “projection_weights”, if defined.
835 const ConstTensorPin recurrentToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 5, model, data);
836 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
837 const ConstTensorPin cellToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 9, model, data);
838 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
839 const ConstTensorPin cellToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 10, model, data);
840 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
841 const ConstTensorPin cellToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 11, model, data);
842 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
843 const ConstTensorPin inputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 12, model, data);
844 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
845 // [output_size, num_units].
846 const ConstTensorPin projectionWeightsPin = ConvertOperationInputToConstTensorPin(operation, 16, model, data);
847 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
848 const ConstTensorPin projectionBiasPin = ConvertOperationInputToConstTensorPin(operation, 17, model, data);
849
850 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
851 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
852 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
853 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
854 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
855 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
856 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
857 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
858 {
859 return Fail("%s: Operation has invalid tensor inputs", __func__);
860 }
861
862 // Get the mandatory input scalars (actually 1-D tensors of size 1):
863 // 20: The activation function: A value indicating the activation function:
864 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
865 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
866 // If set to 0.0 then clipping is disabled.
867 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
868 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
869 ActivationFn activation;
870 float cellClip;
871 float projClip;
872 if (!GetInputActivationFunctionFromTensor(operation, 20, activation, model, data) ||
873 !GetInputScalar(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
874 !GetInputScalar(operation, 22, OperandType::FLOAT32, projClip, model, data))
875 {
876 return Fail("%s: Operation has invalid scalar inputs", __func__);
877 }
878
879 // Outputs:
880 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
881 // CIFG, or [batch_size, num_units * 3] without CIFG.
882 const Operand* scratchBuffer = GetOutputOperand(operation, 0, model);
883 if (!scratchBuffer)
884 {
885 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
886 }
887 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
888 const Operand* outputStateOut = GetOutputOperand(operation, 1, model);
889 if (!outputStateOut)
890 {
891 return Fail("%s: Could not read output 1: outputStateOut", __func__);
892 }
893 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
894 const Operand* cellStateOut = GetOutputOperand(operation, 2, model);
895 if (!cellStateOut)
896 {
897 return Fail("%s: Could not read output 2: cellStateOut", __func__);
898 }
899 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
900 // effectively the same as the current “output state (out)” value.
901 const Operand* output = GetOutputOperand(operation, 3, model);
902 if (!output)
903 {
904 return Fail("%s: Could not read output 3: output", __func__);
905 }
906
907 // set the params structure for the AddLstmLayer call
908 armnn::LstmInputParams params;
909 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
910 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
911 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
912 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
913 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
914 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
915 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
916 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
917 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
918 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
919 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
920 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
921 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
922 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
923 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
924 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
925 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
926
927 // set the layer descriptor
928 armnn::LstmDescriptor desc;
929 desc.m_ActivationFunc = activation;
930 desc.m_ClippingThresCell = cellClip;
931 desc.m_ClippingThresProj = projClip;
932 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
933 params.m_RecurrentToInputWeights == nullptr ||
934 params.m_InputGateBias == nullptr);
935 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
936 params.m_CellToOutputWeights != nullptr);
937 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
938
939 // validate the optional input groups
940 if (desc.m_CifgEnabled &&
941 (params.m_InputToInputWeights != nullptr ||
942 params.m_RecurrentToInputWeights != nullptr ||
943 params.m_InputGateBias != nullptr))
944 {
945 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
946 " and input gate bias must be provided", __func__);
947 }
948
949 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
950 {
951 return Fail("%s: projection bias should not be provided without projection weights", __func__);
952 }
953
954 if (desc.m_PeepholeEnabled &&
955 (params.m_CellToForgetWeights == nullptr ||
956 params.m_CellToOutputWeights == nullptr ||
957 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
958 {
959 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
960 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
961 }
962
963 // Check if the layer is supported
964 // Inputs
965 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
966 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
967 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
968
969 // Outputs
970 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
971 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
972 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
973 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
974
975 // Basic parameters
976 const armnn::TensorInfo& inputToForgetWeights = params.m_InputToForgetWeights->GetInfo();
977 const armnn::TensorInfo& inputToCellWeights = params.m_InputToCellWeights->GetInfo();
978 const armnn::TensorInfo& inputToOutputWeights = params.m_InputToOutputWeights->GetInfo();
979 const armnn::TensorInfo& recurrentToForgetWeights = params.m_RecurrentToForgetWeights->GetInfo();
980 const armnn::TensorInfo& recurrentToCellWeights = params.m_RecurrentToCellWeights->GetInfo();
981 const armnn::TensorInfo& recurrentToOutputWeights = params.m_RecurrentToOutputWeights->GetInfo();
982 const armnn::TensorInfo& forgetGateBias = params.m_ForgetGateBias->GetInfo();
983 const armnn::TensorInfo& cellBias = params.m_CellBias->GetInfo();
984 const armnn::TensorInfo& outputGateBias = params.m_OutputGateBias->GetInfo();
985
986 //Optional parameters
987 const armnn::TensorInfo* inputToInputWeights = nullptr;
988 const armnn::TensorInfo* recurrentToInputWeights = nullptr;
989 const armnn::TensorInfo* cellToInputWeights = nullptr;
990 const armnn::TensorInfo* inputGateBias = nullptr;
991 const armnn::TensorInfo* projectionWeights = nullptr;
992 const armnn::TensorInfo* projectionBias = nullptr;
993 const armnn::TensorInfo* cellToForgetWeights = nullptr;
994 const armnn::TensorInfo* cellToOutputWeights = nullptr;
995
996 if(!desc.m_CifgEnabled)
997 {
998 inputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
999 recurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1000 if (params.m_CellToInputWeights != nullptr)
1001 {
1002 cellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
1003 }
1004 inputGateBias = &(params.m_InputGateBias->GetInfo());
1005 }
1006
1007 if(desc.m_ProjectionEnabled)
1008 {
1009 projectionWeights = &(params.m_ProjectionWeights->GetInfo());
1010 if (params.m_ProjectionBias != nullptr)
1011 {
1012 projectionBias = &(params.m_ProjectionBias->GetInfo());
1013 }
1014 }
1015
1016 if(desc.m_PeepholeEnabled)
1017 {
1018 cellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
1019 cellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
1020 }
1021
1022 if (!IsLayerSupported(__func__,
1023 armnn::IsLstmSupported,
1024 data.m_Compute,
1025 inputInfo,
1026 outputStateInInfo,
1027 cellStateInInfo,
1028 scratchBufferInfo,
1029 outputStateOutInfo,
1030 cellStateOutInfo,
1031 outputInfo,
1032 desc,
1033 inputToForgetWeights,
1034 inputToCellWeights,
1035 inputToOutputWeights,
1036 recurrentToForgetWeights,
1037 recurrentToCellWeights,
1038 recurrentToOutputWeights,
1039 forgetGateBias,
1040 cellBias,
1041 outputGateBias,
1042 inputToInputWeights,
1043 recurrentToInputWeights,
1044 cellToInputWeights,
1045 inputGateBias,
1046 projectionWeights,
1047 projectionBias,
1048 cellToForgetWeights,
1049 cellToOutputWeights))
1050 {
1051 return false;
1052 }
1053
1054 // Add the layer
1055 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
1056
1057 input.Connect(layer->GetInputSlot(0));
1058 outputStateIn.Connect(layer->GetInputSlot(1));
1059 cellStateIn.Connect(layer->GetInputSlot(2));
1060
1061 return (SetupAndTrackLayerOutputSlot(operation, 0, *layer, 0, model, data) &&
1062 SetupAndTrackLayerOutputSlot(operation, 1, *layer, 1, model, data) &&
1063 SetupAndTrackLayerOutputSlot(operation, 2, *layer, 2, model, data) &&
1064 SetupAndTrackLayerOutputSlot(operation, 3, *layer, 3, model, data));
1065}
1066
1067bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1068{
1069 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1070 if (!input.IsValid())
1071 {
1072 return Fail("%s: Operation has invalid inputs", __func__);
1073 }
1074
1075 const Operand* output = GetOutputOperand(operation, 0, model);
1076 if (!output)
1077 {
1078 return Fail("%s: Could not read output 0", __func__);
1079 }
1080
1081 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1082 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1083
1084 const armnn::TensorInfo swizzledInputInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
1085 const armnn::TensorInfo swizzledOutputInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
1086
1087 if (!IsLayerSupported(__func__,
1088 armnn::IsL2NormalizationSupported,
1089 data.m_Compute,
1090 swizzledInputInfo,
1091 swizzledOutputInfo))
1092 {
1093 return false;
1094 }
1095
1096 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer();
1097 assert(layer != nullptr);
1098 layer->GetOutputSlot(0).SetTensorInfo(swizzledOutputInfo);
1099
1100 armnn::IConnectableLayer& outSwizzleLayer = SwizzleInDeswizzleOut(*data.m_Network, input, *layer);
1101
1102 return SetupAndTrackLayerOutputSlot(operation, 0, outSwizzleLayer, model, data);
1103}
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
1330 const armnn::TensorInfo swizzledInputInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
1331 const armnn::TensorInfo swizzledOutputInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
1332
1333 if (!IsLayerSupported(__func__,
1334 armnn::IsResizeBilinearSupported,
1335 data.m_Compute,
1336 swizzledInputInfo))
1337 {
1338 return false;
1339 }
1340
1341 armnn::ResizeBilinearDescriptor desc;
1342
1343 if ( !GetInputScalar(operation, 1, OperandType::INT32, desc.m_TargetHeight, model, data)
1344 || !GetInputScalar(operation, 2, OperandType::INT32, desc.m_TargetWidth, model, data))
1345 {
1346 return Fail("%s: Operation has invalid inputs", __func__);
1347 }
1348
1349 armnn::IConnectableLayer* layer = data.m_Network->AddResizeBilinearLayer(desc);
1350 assert(layer != nullptr);
1351 layer->GetOutputSlot(0).SetTensorInfo(swizzledOutputInfo);
1352
1353 armnn::IConnectableLayer& outSwizzleLayer = SwizzleInDeswizzleOut(*data.m_Network, input, *layer);
1354
1355 return SetupAndTrackLayerOutputSlot(operation, 0, outSwizzleLayer, model, data);
1356
1357}
1358
1359} // namespace hal_1_0
1360} // namespace armnn_driver