blob: 4c5fba3f82be026dd486285083b665e4b80a1e96 [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
357 const armnn::TensorInfo swizzledInputInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
358 const armnn::TensorInfo swizzledOutputInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
359
360 // ArmNN does not currently support non-fixed weights or bias
361 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data, NHWCToArmNN);
362 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data);
363
364 if (!weightsPin.IsValid() || !biasPin.IsValid())
365 {
366 return Fail("%s: Operation has invalid inputs", __func__);
367 }
368
369 armnn::ConstTensor weights = weightsPin.GetConstTensor();
370 armnn::ConstTensor bias = biasPin.GetConstTensor();
371 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), swizzledInputInfo);
372
373 armnn::Convolution2dDescriptor desc;
374 ActivationFn activation;
375
376 if (operation.inputs.size() == 10)
377 {
378 if (!GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
379 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
380 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
381 !GetInputScalar(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
382 !GetInputScalar(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
383 !GetInputScalar(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
384 !GetInputActivationFunction(operation, 9, activation, model, data))
385 {
386 return Fail("%s: Operation has invalid inputs", __func__);
387 }
388 }
389 else if (operation.inputs.size() == 7)
390 {
391 android::nn::PaddingScheme paddingScheme;
392 if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) ||
393 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
394 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
395 !GetInputActivationFunction(operation, 6, activation, model, data))
396 {
397 return Fail("%s: Operation has invalid inputs", __func__);
398 }
399
400 const uint32_t kernelX = weights.GetShape()[3];
401 const uint32_t kernelY = weights.GetShape()[2];
402 const uint32_t inputX = swizzledInputInfo.GetShape()[3];
403 const uint32_t inputY = swizzledInputInfo.GetShape()[2];
404
405 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
406 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
407 }
408 else
409 {
410 return Fail("%s: Unsupported number of operation inputs", __func__);
411 }
412
413 desc.m_BiasEnabled = true;
arovir015602b192018-10-04 16:15:02 +0100414 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100415
416 if (!IsLayerSupported(__func__,
417 armnn::IsConvolution2dSupported,
418 data.m_Compute,
419 swizzledInputInfo,
420 swizzledOutputInfo,
421 desc,
422 weights.GetInfo(),
423 biases))
424 {
425 return false;
426 }
427
428 armnn::IConnectableLayer* startLayer = data.m_Network->AddConvolution2dLayer(desc, weights, bias);
429 armnn::IConnectableLayer* endLayer = ProcessActivation(swizzledOutputInfo, activation, startLayer, data);
430
431 if (endLayer != nullptr)
432 {
433 armnn::IConnectableLayer& outSwizzleLayer =
434 SwizzleInDeswizzleOut(*data.m_Network, input, *startLayer, *endLayer);
435 return SetupAndTrackLayerOutputSlot(operation, 0, outSwizzleLayer, model, data);
436 }
437 else
438 {
439 return Fail("%s: ProcessActivation failed", __func__);
440 }
441}
442
443bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data)
444{
445 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
446 if (!input.IsValid())
447 {
448 return Fail("%s: Operation has invalid inputs", __func__);
449 }
450
451 const Operand* output = GetOutputOperand(operation, 0, model);
452 if (!output)
453 {
454 return Fail("%s: Could not read output 0", __func__);
455 }
456
457 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
458 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
459
460 const armnn::TensorInfo swizzledInputInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
461 const armnn::TensorInfo swizzledOutputInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
462
463 // ArmNN does not currently support non-fixed weights or bias
464
465 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
466 // but in ArmNN it needs to be [ M, I, H, W ]
467 const Operand* weightsOperand = GetInputOperand(operation, 1, model);
468
469 if (weightsOperand == nullptr)
470 {
471 return Fail("%s: Operand is invalid", __func__);
472 }
473
474 // Reinterpret weight data as [ H, W, I, M ]
475 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1], weightsOperand->dimensions[2],
476 inputInfo.GetShape()[3],
477 weightsOperand->dimensions[3] / inputInfo.GetShape()[3] });
478
479 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
480 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
481 ConstTensorPin weightsPin =
482 ConvertOperationInputToConstTensorPin(operation, 1, model, data, HWIMToMIHW, &weightsShape);
483
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();
494 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), swizzledInputInfo);
495
496 armnn::DepthwiseConvolution2dDescriptor desc;
497 ActivationFn activation;
498
499 if (operation.inputs.size() == 11)
500 {
501 if (!GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) ||
502 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) ||
503 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) ||
504 !GetInputScalar(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) ||
505 !GetInputScalar(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) ||
506 !GetInputScalar(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) ||
507 !GetInputActivationFunction(operation, 10, activation, model, data))
508 {
509 return Fail("%s: Operation has invalid inputs", __func__);
510 }
511 }
512 else if (operation.inputs.size() == 8)
513 {
514 android::nn::PaddingScheme paddingScheme;
515 if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) ||
516 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) ||
517 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) ||
518 !GetInputActivationFunction(operation, 7, activation, model, data))
519 {
520 return Fail("%s: Operation has invalid inputs", __func__);
521 }
522
523 const uint32_t kernelX = weights.GetShape()[3];
524 const uint32_t kernelY = weights.GetShape()[2];
525 const uint32_t inputX = swizzledInputInfo.GetShape()[3];
526 const uint32_t inputY = swizzledInputInfo.GetShape()[2];
527
528 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
529 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
530 }
531 else
532 {
533 return Fail("%s: Unsupported number of operation inputs", __func__);
534 }
535
536 desc.m_BiasEnabled = true;
arovir015602b192018-10-04 16:15:02 +0100537 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100538
539 if (!IsLayerSupported(__func__,
540 armnn::IsDepthwiseConvolutionSupported,
541 data.m_Compute,
542 swizzledInputInfo,
543 swizzledOutputInfo,
544 desc,
545 weights.GetInfo(),
546 biases))
547 {
548 return false;
549 }
550
551 armnn::IConnectableLayer* startLayer = data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, bias);
552 armnn::IConnectableLayer* endLayer = ProcessActivation(swizzledOutputInfo, activation, startLayer, data);
553
554 if (endLayer != nullptr)
555 {
556 armnn::IConnectableLayer& outSwizzleLayer =
557 SwizzleInDeswizzleOut(*data.m_Network, input, *startLayer, *endLayer);
558 return SetupAndTrackLayerOutputSlot(operation, 0, outSwizzleLayer, model, data);
559 }
560 else
561 {
562 return Fail("%s: ProcessActivation failed", __func__);
563 }
564}
565
566bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
567{
568 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
569 if (!input.IsValid())
570 {
571 return Fail("%s: Operation has invalid inputs", __func__);
572 }
573
574 const Operand* const outputOperand = GetOutputOperand(operation, 0, model);
575 if (!outputOperand)
576 {
577 return Fail("%s: Operation has invalid outputs", __func__);
578 }
579
580 if (!IsLayerSupported(__func__,
581 armnn::IsFloorSupported,
582 data.m_Compute,
583 input.GetTensorInfo(),
584 GetTensorInfoForOperand(*outputOperand)))
585 {
586 return false;
587 }
588
589 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
590 assert(layer != nullptr);
591 input.Connect(layer->GetInputSlot(0));
592
593 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
594}
595
596bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
597{
598 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
599 if (!input.IsValid())
600 {
601 return Fail("%s: Operation has invalid inputs", __func__);
602 }
603
604 const Operand* output = GetOutputOperand(operation, 0, model);
605 if (!output)
606 {
607 return Fail("%s: Could not read output 0", __func__);
608 }
609
610 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
611 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
612
613 // ArmNN does not currently support non-fixed weights or bias
614 ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data); // 2D
615 ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); // 1D
616
617 if (!weightsPin.IsValid() || !biasPin.IsValid())
618 {
619 return Fail("%s: Operation has invalid inputs", __func__);
620 }
621
622 armnn::ConstTensor weights = weightsPin.GetConstTensor();
623 armnn::ConstTensor bias = biasPin.GetConstTensor();
624
625 armnn::TensorInfo reshapedInfo = inputInfo;
626 if (inputInfo.GetNumDimensions() > 2U)
627 {
628 unsigned int dim0 = inputInfo.GetShape()[0];
629 unsigned int dim1 = inputInfo.GetShape()[1];
630
631 for (unsigned int i = 2U; i < inputInfo.GetNumDimensions(); ++i)
632 {
633 dim1 *= inputInfo.GetShape()[i];
634 }
635
636 unsigned int divisor = weights.GetInfo().GetShape()[1] / dim1;
637 if(dim0 % divisor != 0)
638 {
639 return Fail("%s: Failed to deduce tensor shape", __func__);
640 }
641
642 reshapedInfo.SetShape(armnn::TensorShape({dim0 / divisor, dim1 * divisor}));
643 }
644
645 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
646 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
647
648 ActivationFn activationFunction;
649 if (!GetInputActivationFunction(operation, 3, activationFunction, model, data))
650 {
651 return Fail("%s: Operation has invalid inputs", __func__);
652 }
653
654 armnn::FullyConnectedDescriptor desc;
655 desc.m_TransposeWeightMatrix = true;
656 desc.m_BiasEnabled = true;
657
658 if (!IsLayerSupported(__func__,
659 armnn::IsFullyConnectedSupported,
660 data.m_Compute,
661 inputInfo,
662 outputInfo,
663 weights.GetInfo(),
664 bias.GetInfo(),
665 desc))
666 {
667 return false;
668 }
669
670 armnn::IConnectableLayer* startLayer = data.m_Network->AddFullyConnectedLayer(desc, weights, bias);
671 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
672
673 if (endLayer != nullptr)
674 {
675 if (inputInfo.GetNumDimensions() > 2U)
676 {
677 armnn::ReshapeDescriptor reshapeDescriptor;
678 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
679
680 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
681 assert(reshapeLayer != nullptr);
682 input.Connect(reshapeLayer->GetInputSlot(0));
683 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
684 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
685 }
686 else
687 {
688 input.Connect(startLayer->GetInputSlot(0));
689 }
690
691 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
692 }
693 else
694 {
695 return Fail("%s: ProcessActivation failed", __func__);
696 }
697}
698
699bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
700 const Model& model,
701 ConversionData& data)
702{
703 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
704 if (!input.IsValid())
705 {
706 return Fail("%s: Operation has invalid inputs", __func__);
707 }
708
709 const Operand* output = GetOutputOperand(operation, 0, model);
710 if (!output)
711 {
712 return Fail("%s: Could not read output 0", __func__);
713 }
714
narpra012fb804a2018-10-22 14:52:32 +0100715 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
arovir01b0717b52018-09-05 17:03:25 +0100716 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
717
arovir01b0717b52018-09-05 17:03:25 +0100718 armnn::NormalizationDescriptor descriptor;
719
narpra012fb804a2018-10-22 14:52:32 +0100720 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +0100721 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
narpra012fb804a2018-10-22 14:52:32 +0100722 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
arovir01b0717b52018-09-05 17:03:25 +0100723
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,
narpra012fb804a2018-10-22 14:52:32 +0100740 inputInfo,
741 outputInfo,
arovir01b0717b52018-09-05 17:03:25 +0100742 descriptor))
743 {
744 return false;
745 }
746
747
748 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
749 assert(layer != nullptr);
narpra012fb804a2018-10-22 14:52:32 +0100750 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +0100751
narpra012fb804a2018-10-22 14:52:32 +0100752 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100753}
754
755bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
756{
757 armnn::ActivationDescriptor desc;
758 desc.m_Function = armnn::ActivationFunction::Sigmoid;
759
760 return ConvertToActivation(operation, __func__, desc, model, data);
761}
762
763bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
764{
765 // Inputs:
766 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
767 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
768 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
769 if (!input.IsValid())
770 {
771 return Fail("%s: Could not read input 0: input", __func__);
772 }
773 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
774 LayerInputHandle outputStateIn = ConvertToLayerInputHandle(operation, 18, model, data);
775 if (!outputStateIn.IsValid())
776 {
777 return Fail("%s: Could not read input 18: outputStateIn", __func__);
778 }
779 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
780 LayerInputHandle cellStateIn = ConvertToLayerInputHandle(operation, 19, model, data);
781 if (!cellStateIn.IsValid())
782 {
783 return Fail("%s: Could not read input 19: cellStateIn", __func__);
784 }
785
786 // Get the mandatory input tensors:
787 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
788 // [num_units, input_size].
789 const ConstTensorPin inputToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data);
790 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units, input_size].
791 const ConstTensorPin inputToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 3, model, data);
792 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
793 // [num_units, input_size].
794 const ConstTensorPin inputToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 4, model, data);
795 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
796 // [num_units, output_size].
797 const ConstTensorPin recurrentToForgetWeightsPin =
798 ConvertOperationInputToConstTensorPin(operation, 6, model, data);
799 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
800 // [num_units, output_size].
801 const ConstTensorPin recurrentToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 7, model, data);
802 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
803 // [num_units, output_size].
804 const ConstTensorPin recurrentToOutputWeightsPin =
805 ConvertOperationInputToConstTensorPin(operation, 8, model, data);
806 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
807 const ConstTensorPin forgetGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 13, model, data);
808 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
809 const ConstTensorPin cellBiasPin = ConvertOperationInputToConstTensorPin(operation, 14, model, data);
810 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
811 const ConstTensorPin outputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 15, model, data);
812
813 if (!inputToForgetWeightsPin.IsValid() ||
814 !inputToCellWeightsPin.IsValid() ||
815 !inputToOutputWeightsPin.IsValid() ||
816 !recurrentToForgetWeightsPin.IsValid() ||
817 !recurrentToCellWeightsPin.IsValid() ||
818 !recurrentToOutputWeightsPin.IsValid() ||
819 !forgetGateBiasPin.IsValid() ||
820 !cellBiasPin.IsValid() ||
821 !outputGateBiasPin.IsValid())
822 {
823 return Fail("%s: Operation has invalid tensor inputs", __func__);
824 }
825
826 // Get the optional input tensors:
827 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
828 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
829 const ConstTensorPin inputToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data);
830 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
831 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
832 // “num_units”), or the second dimension of the “projection_weights”, if defined.
833 const ConstTensorPin recurrentToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 5, model, data);
834 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
835 const ConstTensorPin cellToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 9, model, data);
836 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
837 const ConstTensorPin cellToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 10, model, data);
838 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
839 const ConstTensorPin cellToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 11, model, data);
840 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
841 const ConstTensorPin inputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 12, model, data);
842 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
843 // [output_size, num_units].
844 const ConstTensorPin projectionWeightsPin = ConvertOperationInputToConstTensorPin(operation, 16, model, data);
845 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
846 const ConstTensorPin projectionBiasPin = ConvertOperationInputToConstTensorPin(operation, 17, model, data);
847
848 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
849 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
850 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
851 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
852 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
853 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
854 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
855 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
856 {
857 return Fail("%s: Operation has invalid tensor inputs", __func__);
858 }
859
860 // Get the mandatory input scalars (actually 1-D tensors of size 1):
861 // 20: The activation function: A value indicating the activation function:
862 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
863 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
864 // If set to 0.0 then clipping is disabled.
865 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
866 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
867 ActivationFn activation;
868 float cellClip;
869 float projClip;
870 if (!GetInputActivationFunctionFromTensor(operation, 20, activation, model, data) ||
871 !GetInputScalar(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
872 !GetInputScalar(operation, 22, OperandType::FLOAT32, projClip, model, data))
873 {
874 return Fail("%s: Operation has invalid scalar inputs", __func__);
875 }
876
877 // Outputs:
878 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with
879 // CIFG, or [batch_size, num_units * 3] without CIFG.
880 const Operand* scratchBuffer = GetOutputOperand(operation, 0, model);
881 if (!scratchBuffer)
882 {
883 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
884 }
885 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
886 const Operand* outputStateOut = GetOutputOperand(operation, 1, model);
887 if (!outputStateOut)
888 {
889 return Fail("%s: Could not read output 1: outputStateOut", __func__);
890 }
891 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
892 const Operand* cellStateOut = GetOutputOperand(operation, 2, model);
893 if (!cellStateOut)
894 {
895 return Fail("%s: Could not read output 2: cellStateOut", __func__);
896 }
897 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
898 // effectively the same as the current “output state (out)” value.
899 const Operand* output = GetOutputOperand(operation, 3, model);
900 if (!output)
901 {
902 return Fail("%s: Could not read output 3: output", __func__);
903 }
904
905 // set the params structure for the AddLstmLayer call
906 armnn::LstmInputParams params;
907 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
908 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
909 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
910 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
911 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
912 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
913 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
914 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
915 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
916 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
917 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
918 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
919 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
920 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
921 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
922 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
923 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
924
925 // set the layer descriptor
926 armnn::LstmDescriptor desc;
927 desc.m_ActivationFunc = activation;
928 desc.m_ClippingThresCell = cellClip;
929 desc.m_ClippingThresProj = projClip;
930 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
931 params.m_RecurrentToInputWeights == nullptr ||
932 params.m_InputGateBias == nullptr);
933 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
934 params.m_CellToOutputWeights != nullptr);
935 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
936
937 // validate the optional input groups
938 if (desc.m_CifgEnabled &&
939 (params.m_InputToInputWeights != nullptr ||
940 params.m_RecurrentToInputWeights != nullptr ||
941 params.m_InputGateBias != nullptr))
942 {
943 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
944 " and input gate bias must be provided", __func__);
945 }
946
947 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
948 {
949 return Fail("%s: projection bias should not be provided without projection weights", __func__);
950 }
951
952 if (desc.m_PeepholeEnabled &&
953 (params.m_CellToForgetWeights == nullptr ||
954 params.m_CellToOutputWeights == nullptr ||
955 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
956 {
957 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
958 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
959 }
960
961 // Check if the layer is supported
962 // Inputs
963 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
964 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
965 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
966
967 // Outputs
968 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
969 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
970 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
971 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
972
973 // Basic parameters
974 const armnn::TensorInfo& inputToForgetWeights = params.m_InputToForgetWeights->GetInfo();
975 const armnn::TensorInfo& inputToCellWeights = params.m_InputToCellWeights->GetInfo();
976 const armnn::TensorInfo& inputToOutputWeights = params.m_InputToOutputWeights->GetInfo();
977 const armnn::TensorInfo& recurrentToForgetWeights = params.m_RecurrentToForgetWeights->GetInfo();
978 const armnn::TensorInfo& recurrentToCellWeights = params.m_RecurrentToCellWeights->GetInfo();
979 const armnn::TensorInfo& recurrentToOutputWeights = params.m_RecurrentToOutputWeights->GetInfo();
980 const armnn::TensorInfo& forgetGateBias = params.m_ForgetGateBias->GetInfo();
981 const armnn::TensorInfo& cellBias = params.m_CellBias->GetInfo();
982 const armnn::TensorInfo& outputGateBias = params.m_OutputGateBias->GetInfo();
983
984 //Optional parameters
985 const armnn::TensorInfo* inputToInputWeights = nullptr;
986 const armnn::TensorInfo* recurrentToInputWeights = nullptr;
987 const armnn::TensorInfo* cellToInputWeights = nullptr;
988 const armnn::TensorInfo* inputGateBias = nullptr;
989 const armnn::TensorInfo* projectionWeights = nullptr;
990 const armnn::TensorInfo* projectionBias = nullptr;
991 const armnn::TensorInfo* cellToForgetWeights = nullptr;
992 const armnn::TensorInfo* cellToOutputWeights = nullptr;
993
994 if(!desc.m_CifgEnabled)
995 {
996 inputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
997 recurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
998 if (params.m_CellToInputWeights != nullptr)
999 {
1000 cellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
1001 }
1002 inputGateBias = &(params.m_InputGateBias->GetInfo());
1003 }
1004
1005 if(desc.m_ProjectionEnabled)
1006 {
1007 projectionWeights = &(params.m_ProjectionWeights->GetInfo());
1008 if (params.m_ProjectionBias != nullptr)
1009 {
1010 projectionBias = &(params.m_ProjectionBias->GetInfo());
1011 }
1012 }
1013
1014 if(desc.m_PeepholeEnabled)
1015 {
1016 cellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
1017 cellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
1018 }
1019
1020 if (!IsLayerSupported(__func__,
1021 armnn::IsLstmSupported,
1022 data.m_Compute,
1023 inputInfo,
1024 outputStateInInfo,
1025 cellStateInInfo,
1026 scratchBufferInfo,
1027 outputStateOutInfo,
1028 cellStateOutInfo,
1029 outputInfo,
1030 desc,
1031 inputToForgetWeights,
1032 inputToCellWeights,
1033 inputToOutputWeights,
1034 recurrentToForgetWeights,
1035 recurrentToCellWeights,
1036 recurrentToOutputWeights,
1037 forgetGateBias,
1038 cellBias,
1039 outputGateBias,
1040 inputToInputWeights,
1041 recurrentToInputWeights,
1042 cellToInputWeights,
1043 inputGateBias,
1044 projectionWeights,
1045 projectionBias,
1046 cellToForgetWeights,
1047 cellToOutputWeights))
1048 {
1049 return false;
1050 }
1051
1052 // Add the layer
1053 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
1054
1055 input.Connect(layer->GetInputSlot(0));
1056 outputStateIn.Connect(layer->GetInputSlot(1));
1057 cellStateIn.Connect(layer->GetInputSlot(2));
1058
1059 return (SetupAndTrackLayerOutputSlot(operation, 0, *layer, 0, model, data) &&
1060 SetupAndTrackLayerOutputSlot(operation, 1, *layer, 1, model, data) &&
1061 SetupAndTrackLayerOutputSlot(operation, 2, *layer, 2, model, data) &&
1062 SetupAndTrackLayerOutputSlot(operation, 3, *layer, 3, model, data));
1063}
1064
1065bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
1066{
1067 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1068 if (!input.IsValid())
1069 {
1070 return Fail("%s: Operation has invalid inputs", __func__);
1071 }
1072
1073 const Operand* output = GetOutputOperand(operation, 0, model);
1074 if (!output)
1075 {
1076 return Fail("%s: Could not read output 0", __func__);
1077 }
1078
1079 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1080 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1081
Matteo Martincigh58f71092018-09-25 15:58:52 +01001082 armnn::L2NormalizationDescriptor desc;
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +01001083 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matteo Martincigh58f71092018-09-25 15:58:52 +01001084
arovir01b0717b52018-09-05 17:03:25 +01001085 if (!IsLayerSupported(__func__,
1086 armnn::IsL2NormalizationSupported,
1087 data.m_Compute,
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +01001088 inputInfo,
1089 outputInfo,
Matteo Martincigh58f71092018-09-25 15:58:52 +01001090 desc))
arovir01b0717b52018-09-05 17:03:25 +01001091 {
1092 return false;
1093 }
1094
Matteo Martincigh58f71092018-09-25 15:58:52 +01001095 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer(desc);
arovir01b0717b52018-09-05 17:03:25 +01001096 assert(layer != nullptr);
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +01001097 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +01001098
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +01001099 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001100}
1101
1102bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1103{
1104 return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
1105}
1106
1107bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1108{
1109 return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
1110}
1111
1112bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1113{
1114 LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data);
1115 LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data);
1116
1117 if (!input0.IsValid() || !input1.IsValid())
1118 {
1119 return Fail("%s: Operation has invalid inputs", __func__);
1120 }
1121
1122 // The FuseActivation parameter is always the input index 2
1123 // and it should be optional
1124 ActivationFn activationFunction;
1125 if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data))
1126 {
1127 return Fail("%s: Operation has invalid inputs", __func__);
1128 }
1129
1130 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
1131
1132 if (outputOperand == nullptr)
1133 {
1134 return false;
1135 }
1136
1137 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
1138
1139 if (!IsLayerSupported(__func__,
1140 armnn::IsMultiplicationSupported,
1141 data.m_Compute,
1142 input0.GetTensorInfo(),
1143 input1.GetTensorInfo(),
1144 outInfo))
1145 {
1146 return false;
1147 }
1148
1149 armnn::IConnectableLayer* const startLayer = data.m_Network->AddMultiplicationLayer();
1150 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
1151
1152 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
1153 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
1154
1155 if (endLayer != nullptr)
1156 {
1157 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
1158 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
1159 }
1160 else
1161 {
1162 return Fail("%s: ProcessActivation failed", __func__);
1163 }
1164}
1165
1166bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1167{
1168 armnn::ActivationDescriptor desc;
1169 desc.m_Function = armnn::ActivationFunction::ReLu;
1170
1171 return ConvertToActivation(operation, __func__, desc, model, data);
1172}
1173
1174bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1175{
1176 armnn::ActivationDescriptor desc;
1177 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1178 desc.m_A = 1.0f;
1179 desc.m_B = -1.0f;
1180
1181 return ConvertToActivation(operation, __func__, desc, model, data);
1182}
1183
1184bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1185{
1186 armnn::ActivationDescriptor desc;
1187 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1188 desc.m_A = 6.0f;
1189
1190 return ConvertToActivation(operation, __func__, desc, model, data);
1191}
1192
1193bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1194{
1195 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1196 if (!input.IsValid())
1197 {
1198 return Fail("%s: Operation has invalid inputs", __func__);
1199 }
1200
1201 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
1202 if (!outputOperand)
1203 {
1204 return Fail("%s: Operation has no outputs", __func__);
1205 }
1206
1207 const armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
1208
1209 armnn::SoftmaxDescriptor desc;
1210 if (!GetInputFloat32(operation, 1, desc.m_Beta, model, data))
1211 {
1212 return Fail("%s: Operation has invalid inputs", __func__);
1213 }
1214
1215 if (!IsLayerSupported(__func__,
1216 armnn::IsSoftmaxSupported,
1217 data.m_Compute,
1218 input.GetTensorInfo(),
1219 outInfo,
1220 desc))
1221 {
1222 return false;
1223 }
1224
1225 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1226 assert(layer != nullptr);
1227 input.Connect(layer->GetInputSlot(0));
1228
1229 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
1230}
1231
1232bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1233{
1234 armnn::ActivationDescriptor desc;
1235 desc.m_Function = armnn::ActivationFunction::TanH;
1236 desc.m_A = 1.0f; // android nn does not support tanH parameters
1237 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1238
1239 return ConvertToActivation(operation, __func__, desc, model, data);
1240}
1241
1242bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1243{
1244 const Operand* inputOperand = GetInputOperand(operation, 0, model);
1245 const Operand* requestedShapeOperand = GetInputOperand(operation, 1, model);
1246 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
1247
1248 if (inputOperand == nullptr
1249 || requestedShapeOperand == nullptr
1250 || outputOperand == nullptr)
1251 {
1252 return Fail("%s: Operation has invalid inputs", __func__);
1253 }
1254
1255
1256 if (requestedShapeOperand->dimensions.size() != 1)
1257 {
1258 return Fail("%s: Input 1 expected to be one-dimensional (found %i dimensions)",
1259 __func__, requestedShapeOperand->dimensions.size());
1260 }
1261
1262 std::vector<int32_t> targetDimensions;
1263 if (!GetTensorInt32Values(*requestedShapeOperand, targetDimensions, model, data))
1264 {
1265 return Fail("%s: Could not read values of input 1", __func__);
1266 }
1267
1268 const Shape inputOperandShape = GetOperandShape(*inputOperand);
1269
1270 Shape requestedShape;
1271 // targetDimensions may contain special values (e.g. -1). reshapePrepare() is an AndroidNN provided utility
1272 // function that resolves these values into a fully specified tensor shape.
1273 if (!reshapePrepare(inputOperandShape, targetDimensions.data(), targetDimensions.size(), &requestedShape))
1274 {
1275 return Fail("%s: Failed to resolve the requested shape", __func__);
1276 }
1277
1278 const Shape outputOperandShape = GetOperandShape(*outputOperand);
1279 if (!SameShape(requestedShape, outputOperandShape))
1280 {
1281 return Fail("%s: Shape of output operand does not match resolved requested shape", __func__);
1282 }
1283
1284 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1285 if (!input.IsValid())
1286 {
1287 return Fail("%s: Could not read input 0", __func__);
1288 }
1289
1290 if (!IsLayerSupported(__func__,
1291 armnn::IsReshapeSupported,
1292 data.m_Compute,
1293 input.GetTensorInfo()))
1294 {
1295 return false;
1296 }
1297
1298
1299 armnn::ReshapeDescriptor reshapeDescriptor;
1300 reshapeDescriptor.m_TargetShape = armnn::TensorShape(requestedShape.dimensions.size(),
1301 requestedShape.dimensions.data());
1302
1303 armnn::IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
1304 assert(layer != nullptr);
1305 input.Connect(layer->GetInputSlot(0));
1306
1307 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
1308}
1309
1310bool HalPolicy::ConvertResizeBilinear(const Operation& operation, const Model& model, ConversionData& data)
1311{
1312 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
1313 if (!input.IsValid())
1314 {
1315 return Fail("%s: Could not read input 0", __func__);
1316 }
1317
1318 const Operand* output = GetOutputOperand(operation, 0, model);
1319 if (!output)
1320 {
1321 return Fail("%s: Could not read output 0", __func__);
1322 }
1323
1324 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1325 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1326
1327 const armnn::TensorInfo swizzledInputInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
1328 const armnn::TensorInfo swizzledOutputInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
1329
1330 if (!IsLayerSupported(__func__,
1331 armnn::IsResizeBilinearSupported,
1332 data.m_Compute,
1333 swizzledInputInfo))
1334 {
1335 return false;
1336 }
1337
1338 armnn::ResizeBilinearDescriptor desc;
1339
1340 if ( !GetInputScalar(operation, 1, OperandType::INT32, desc.m_TargetHeight, model, data)
1341 || !GetInputScalar(operation, 2, OperandType::INT32, desc.m_TargetWidth, model, data))
1342 {
1343 return Fail("%s: Operation has invalid inputs", __func__);
1344 }
1345
1346 armnn::IConnectableLayer* layer = data.m_Network->AddResizeBilinearLayer(desc);
1347 assert(layer != nullptr);
1348 layer->GetOutputSlot(0).SetTensorInfo(swizzledOutputInfo);
1349
1350 armnn::IConnectableLayer& outSwizzleLayer = SwizzleInDeswizzleOut(*data.m_Network, input, *layer);
1351
1352 return SetupAndTrackLayerOutputSlot(operation, 0, outSwizzleLayer, model, data);
1353
1354}
1355
1356} // namespace hal_1_0
Matteo Martincigh58f71092018-09-25 15:58:52 +01001357} // namespace armnn_driver