blob: dacf3b0555dc05cd570e6bd97159ad1eb22812c4 [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
Matthew Benthamf61c2702019-04-23 16:43:27 +01008#include <armnn/Optional.hpp>
9
10#include "FullyConnected.hpp"
arovir015602b192018-10-04 16:15:02 +010011
arovir01b0717b52018-09-05 17:03:25 +010012namespace armnn_driver
13{
14namespace hal_1_0
15{
16
17bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
18{
19 switch (operation.type)
20 {
21 case V1_0::OperationType::ADD:
22 return ConvertAdd(operation, model, data);
23 case V1_0::OperationType::AVERAGE_POOL_2D:
24 return ConvertAveragePool2d(operation, model, data);
25 case V1_0::OperationType::CONCATENATION:
26 return ConvertConcatenation(operation, model, data);
27 case V1_0::OperationType::CONV_2D:
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +010028 return ValidateConv2dParameters(operation) &&
29 ConvertConv2d<hal_1_0::HalPolicy>(operation, model, data);
arovir01b0717b52018-09-05 17:03:25 +010030 case V1_0::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +010031 return ValidateDepthwiseConv2dParameters(operation) &&
32 ConvertDepthwiseConv2d<hal_1_0::HalPolicy>(operation, model, data);
David Monahanacf479a2019-05-29 14:27:04 +010033 case V1_0::OperationType::DEQUANTIZE:
34 return ConvertDequantize(operation, model, data);
arovir01b0717b52018-09-05 17:03:25 +010035 case V1_0::OperationType::FLOOR:
36 return ConvertFloor(operation, model, data);
37 case V1_0::OperationType::FULLY_CONNECTED:
38 return ConvertFullyConnected(operation, model, data);
39 case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
40 return ConvertLocalResponseNormalization(operation, model, data);
41 case V1_0::OperationType::LOGISTIC:
42 return ConvertLogistic(operation, model, data);
43 case V1_0::OperationType::LSTM:
44 return ConvertLstm(operation, model, data);
45 case V1_0::OperationType::L2_NORMALIZATION:
46 return ConvertL2Normalization(operation, model, data);
47 case V1_0::OperationType::L2_POOL_2D:
48 return ConvertL2Pool2d(operation, model, data);
49 case V1_0::OperationType::MAX_POOL_2D:
50 return ConvertMaxPool2d(operation, model, data);
51 case V1_0::OperationType::MUL:
52 return ConvertMul(operation, model, data);
53 case V1_0::OperationType::RELU:
54 return ConvertReLu(operation, model, data);
55 case V1_0::OperationType::RELU1:
56 return ConvertReLu1(operation, model, data);
57 case V1_0::OperationType::RELU6:
58 return ConvertReLu6(operation, model, data);
59 case V1_0::OperationType::SOFTMAX:
60 return ConvertSoftmax(operation, model, data);
61 case V1_0::OperationType::TANH:
62 return ConvertTanH(operation, model, data);
63 case V1_0::OperationType::RESHAPE:
64 return ConvertReshape(operation, model, data);
65 case V1_0::OperationType::RESIZE_BILINEAR:
66 return ConvertResizeBilinear(operation, model, data);
67 default:
68 return Fail("%s: Operation type %s not supported in ArmnnDriver",
69 __func__, toString(operation.type).c_str());
70 }
71}
72
Mike Kellyb5fdf382019-06-11 16:35:25 +010073bool HalPolicy::ValidateConv2dParameters(const Operation &operation)
74{
75 if (operation.inputs.size() != 10 && operation.inputs.size() != 7)
76 {
77 return Fail("%s: Unsupported number of operation inputs", __func__);
78 }
79 return true;
80}
81
82bool HalPolicy::ValidateDepthwiseConv2dParameters(const Operation &operation)
83{
84 if (operation.inputs.size() != 11 && operation.inputs.size() != 8)
85 {
86 return Fail("%s: Unsupported number of operation inputs", __func__);
87 }
88 return true;
89}
90
arovir01b0717b52018-09-05 17:03:25 +010091bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
92{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +010093 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
94 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 1, model, data);
arovir01b0717b52018-09-05 17:03:25 +010095
96 if (!input0.IsValid() || !input1.IsValid())
97 {
98 return Fail("%s: Operation has invalid inputs", __func__);
99 }
100
101 // The FuseActivation parameter is always the input index 2
102 // and it should be optional
103 ActivationFn activationFunction;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100104 if (!GetOptionalInputActivation<hal_1_0::HalPolicy>(operation, 2, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100105 {
106 return Fail("%s: Operation has invalid inputs", __func__);
107 }
108
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100109 const Operand* outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100110 if (!outputOperand)
111 {
112 return false;
113 }
114
115 const armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
116
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100117 if (!IsLayerSupportedForAnyBackend(__func__,
118 armnn::IsAdditionSupported,
119 data.m_Backends,
120 input0.GetTensorInfo(),
121 input1.GetTensorInfo(),
122 outInfo))
arovir01b0717b52018-09-05 17:03:25 +0100123 {
124 return false;
125 }
126
127 armnn::IConnectableLayer* const startLayer = data.m_Network->AddAdditionLayer();
128 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
129
130 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
131 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
132
133 if (endLayer != nullptr)
134 {
135 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100136 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100137 }
138 else
139 {
140 return Fail("%s: ProcessActivation failed", __func__);
141 }
142}
143
144bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
145{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100146 return ConvertPooling2d<hal_1_0::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Average, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100147}
148
149bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
150{
151 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
152 if (operation.inputs.size() <= 1)
153 {
154 return Fail("%s: Operation has insufficient arguments", __func__);
155 }
156
157 // Get inputs and outputs
158 const std::size_t numInputTensors = operation.inputs.size() - 1;
159
160 int32_t concatDim;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100161 if (!GetInputScalar<hal_1_0::HalPolicy>(operation, numInputTensors, OperandType::INT32, concatDim, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100162 {
163 return Fail("%s: Operation has invalid inputs", __func__);
164 }
165
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100166 const Operand* const outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100167 if (!outputOperand)
168 {
169 return Fail("%s: Operation has no outputs", __func__);
170 }
171
172
173 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
174 armnn::TensorShape outputShape = outputInfo.GetShape();
175
176 //
177 // handle negative concat dims along the lines of tensorflow as described here:
178 // https://www.tensorflow.org/api_docs/python/tf/concat
179 // "negative axis refers to axis + rank(values)-th dimension"
180 //
181 if (concatDim < 0)
182 {
183 concatDim += outputShape.GetNumDimensions();
184 }
185
186 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
187 {
188 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
189 }
190
191 std::vector<LayerInputHandle> inputHandles;
192 std::vector<armnn::TensorShape> inputShapes;
193
194 inputHandles.reserve(numInputTensors);
195 inputShapes.reserve(numInputTensors);
196
197 bool inputsHaveBeenReshaped = false;
198 unsigned int tensorDimensionsAdded = 0;
199
200 for (uint32_t i = 0; i < numInputTensors; ++i)
201 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100202 const Operand* const operand = GetInputOperand<hal_1_0::HalPolicy>(operation, i, model);
arovir01b0717b52018-09-05 17:03:25 +0100203 if (!operand)
204 {
205 return Fail("%s: Operation has invalid inputs", __func__);
206 }
207
208 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100209 LayerInputHandle operandInputHandle =
210 ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, i, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100211
212 if (operandShape.GetNumDimensions() == 0)
213 {
214 return Fail("%s: Operands with rank 0 are not supported", __func__);
215 }
216
217 if (RequiresReshape(operandShape))
218 {
219 inputsHaveBeenReshaped = true;
220
221 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
222
223 // Expand the tensor to three dimensions
224 if (operandShape.GetNumDimensions() == 2)
225 {
226 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
227 tensorDimensionsAdded = 1;
228 }
229 else
230 {
231 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
232 tensorDimensionsAdded = 2;
233 }
234
235 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
236 *data.m_Network,
237 operandInputHandle,
238 reshapeInfo
239 );
240
241 // Point to the reshape operation rather then the input operation
242 operandShape = reshapeInfo.GetShape();
243 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
244 }
245
246 inputShapes.emplace_back(operandShape);
247 inputHandles.emplace_back(operandInputHandle);
248
249 if (!inputHandles.back().IsValid())
250 {
251 return Fail("%s: Operation has invalid inputs", __func__);
252 }
253 }
254
255 BOOST_ASSERT(inputShapes.size() == inputHandles.size());
256
257 if (inputsHaveBeenReshaped)
258 {
259 // Adjust the concatenation dimension by the amount of dimensions added (if any)
260 concatDim += tensorDimensionsAdded;
261
262 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
263 if (tensorDimensionsAdded == 1)
264 {
265 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
266 }
267 else if (tensorDimensionsAdded == 2)
268 {
narpra01f176d5a2018-11-18 20:17:48 +0000269 outputShape = armnn::TensorShape({1, 1, outputShape[0]});
arovir01b0717b52018-09-05 17:03:25 +0100270 }
271 }
272
narpra01f176d5a2018-11-18 20:17:48 +0000273 // Check if permutations is required and get the pair of permutations required for the concatenation.
274 // Permutation is required when the concat dimension is 2 for a 4D tensor or 1 for a 3D tensor.
arovir01b0717b52018-09-05 17:03:25 +0100275 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
276 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
277
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100278 bool needPermute =
279 CreateConcatPermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
arovir01b0717b52018-09-05 17:03:25 +0100280
narpra01f176d5a2018-11-18 20:17:48 +0000281 if (needPermute)
282 {
283 outputShape = armnnUtils::Permuted(outputShape, permutationPair.first);
284 }
285
arovir01b0717b52018-09-05 17:03:25 +0100286 outputInfo.SetShape(outputShape);
287
288 // this is no-op for identity swizzles, otherwise it replaces both
289 // the handles and shapes with the swizzled layer output handles and shapes
290 SwizzleInputs(*data.m_Network, inputHandles, inputShapes, permutationPair.first);
291
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100292 // Create an armnn concat layer descriptor - this will also perform validation on the input shapes
293 armnn::OriginsDescriptor concatDescriptor;
narpra01f176d5a2018-11-18 20:17:48 +0000294
arovir01b0717b52018-09-05 17:03:25 +0100295 try
296 {
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100297 // The concat descriptor is always created across the only supported concat dimension
narpra01f176d5a2018-11-18 20:17:48 +0000298 // which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100299 concatDescriptor =
Jim Flynn52aa9352019-05-20 12:52:30 +0100300 armnn::CreateDescriptorForConcatenation(inputShapes.begin(), inputShapes.end(), concatDim);
arovir01b0717b52018-09-05 17:03:25 +0100301 }
302 catch (const armnn::Exception& error)
303 {
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100304 return Fail("%s: Error preparing concat descriptor. %s", __func__, error.what());
arovir01b0717b52018-09-05 17:03:25 +0100305 }
306
307 // Validate the output shape is correct given the input shapes based on the
narpra01f176d5a2018-11-18 20:17:48 +0000308 // only valid concat dimension which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
arovir01b0717b52018-09-05 17:03:25 +0100309 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
310 {
311 return Fail("%s: Error validating the output shape for concat", __func__);
312 }
313
314 std::vector<const armnn::TensorInfo*> inputTensorInfos;
315 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
316 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100317 if (!IsLayerSupportedForAnyBackend(__func__,
Jim Flynn073d7a32019-05-13 13:52:56 +0100318 armnn::IsConcatSupported,
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100319 data.m_Backends,
320 inputTensorInfos,
321 outputInfo,
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100322 concatDescriptor))
arovir01b0717b52018-09-05 17:03:25 +0100323 {
324 return false;
325 }
326
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100327 armnn::IConnectableLayer* layer = data.m_Network->AddConcatLayer(concatDescriptor);
arovir01b0717b52018-09-05 17:03:25 +0100328 assert(layer != nullptr);
329 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
330
331 // Connect inputs to the layer
332 const int numInputSlots = layer->GetNumInputSlots();
333 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
334 for (int i = 0; i < numInputSlots; ++i)
335 {
336 // connect the input directly to the merge (concat) layer
337 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
338 }
339
narpra01f176d5a2018-11-18 20:17:48 +0000340 if (needPermute)
341 {
342 // Add permutation layer and connect the output to it, the permutation becomes the output layer
343 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(*data.m_Network,
344 layer->GetOutputSlot(0),
345 permutationPair.second);
346 layer = &deswizzleLayer;
347 }
arovir01b0717b52018-09-05 17:03:25 +0100348
349 if (inputsHaveBeenReshaped)
350 {
351 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
352
353 // Undo the reshape knowing the amount of dimensions added
354 if (tensorDimensionsAdded == 1)
355 {
356 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
357 afterConcatInfo.GetShape()[2] }));
358 }
359 else if (tensorDimensionsAdded == 2)
360 {
narpra01f176d5a2018-11-18 20:17:48 +0000361 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2] }));
arovir01b0717b52018-09-05 17:03:25 +0100362 }
363
364 layer = &AddReshapeLayer(
365 *data.m_Network,
366 layer->GetOutputSlot(0),
367 afterConcatInfo
368 );
369 }
370
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100371 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100372}
373
David Monahanacf479a2019-05-29 14:27:04 +0100374bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
375{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100376 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
David Monahanacf479a2019-05-29 14:27:04 +0100377
378 if (!input.IsValid())
379 {
380 return Fail("%s: Operation has invalid input", __func__);
381 }
382
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100383 const Operand* const outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
David Monahanacf479a2019-05-29 14:27:04 +0100384 if (!outputOperand)
385 {
386 return Fail("%s: Operation has invalid outputs", __func__);
387 }
388
389 if (!IsLayerSupportedForAnyBackend(__func__,
390 armnn::IsDequantizeSupported,
391 data.m_Backends,
392 input.GetTensorInfo(),
393 GetTensorInfoForOperand(*outputOperand)))
394 {
395 return false;
396 }
397
398 armnn::IConnectableLayer* const layer = data.m_Network->AddDequantizeLayer();
399 assert(layer != nullptr);
400 input.Connect(layer->GetInputSlot(0));
401
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100402 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
David Monahanacf479a2019-05-29 14:27:04 +0100403}
404
arovir01b0717b52018-09-05 17:03:25 +0100405bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
406{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100407 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100408 if (!input.IsValid())
409 {
410 return Fail("%s: Operation has invalid inputs", __func__);
411 }
412
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100413 const Operand* const outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100414 if (!outputOperand)
415 {
416 return Fail("%s: Operation has invalid outputs", __func__);
417 }
418
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100419 if (!IsLayerSupportedForAnyBackend(__func__,
420 armnn::IsFloorSupported,
421 data.m_Backends,
422 input.GetTensorInfo(),
423 GetTensorInfoForOperand(*outputOperand)))
arovir01b0717b52018-09-05 17:03:25 +0100424 {
425 return false;
426 }
427
428 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
429 assert(layer != nullptr);
430 input.Connect(layer->GetInputSlot(0));
431
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100432 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100433}
434
435bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
436{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100437 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100438 if (!input.IsValid())
439 {
440 return Fail("%s: Operation has invalid inputs", __func__);
441 }
442
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100443 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100444 if (!output)
445 {
446 return Fail("%s: Could not read output 0", __func__);
447 }
448
449 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
450 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
451
452 // ArmNN does not currently support non-fixed weights or bias
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100453 ConstTensorPin weightsPin =
454 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 1, model, data); // 2D
455 ConstTensorPin biasPin =
456 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 2, model, data); // 1D
arovir01b0717b52018-09-05 17:03:25 +0100457
458 if (!weightsPin.IsValid() || !biasPin.IsValid())
459 {
460 return Fail("%s: Operation has invalid inputs", __func__);
461 }
462
463 armnn::ConstTensor weights = weightsPin.GetConstTensor();
464 armnn::ConstTensor bias = biasPin.GetConstTensor();
arovir01b0717b52018-09-05 17:03:25 +0100465 armnn::TensorInfo reshapedInfo = inputInfo;
Matthew Benthamf61c2702019-04-23 16:43:27 +0100466
467 try
arovir01b0717b52018-09-05 17:03:25 +0100468 {
Matthew Benthamf61c2702019-04-23 16:43:27 +0100469 reshapedInfo.SetShape(FlattenFullyConnectedInput(inputInfo.GetShape(), weights.GetInfo().GetShape()));
470 } catch (const std::exception &e) {
471 return Fail("%s: %s", __func__, e.what());
arovir01b0717b52018-09-05 17:03:25 +0100472 }
473
474 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
475 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
476
477 ActivationFn activationFunction;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100478 if (!GetInputActivationFunction<hal_1_0::HalPolicy>(operation, 3, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100479 {
480 return Fail("%s: Operation has invalid inputs", __func__);
481 }
482
483 armnn::FullyConnectedDescriptor desc;
484 desc.m_TransposeWeightMatrix = true;
485 desc.m_BiasEnabled = true;
486
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100487 if (!IsLayerSupportedForAnyBackend(__func__,
488 armnn::IsFullyConnectedSupported,
489 data.m_Backends,
490 reshapedInfo,
491 outputInfo,
492 weights.GetInfo(),
493 bias.GetInfo(),
494 desc))
arovir01b0717b52018-09-05 17:03:25 +0100495 {
496 return false;
497 }
498
Matteo Martincighba01f372019-05-14 13:28:21 +0100499 armnn::IConnectableLayer* startLayer =
500 data.m_Network->AddFullyConnectedLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
arovir01b0717b52018-09-05 17:03:25 +0100501 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
502
503 if (endLayer != nullptr)
504 {
505 if (inputInfo.GetNumDimensions() > 2U)
506 {
507 armnn::ReshapeDescriptor reshapeDescriptor;
508 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
509
510 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
511 assert(reshapeLayer != nullptr);
512 input.Connect(reshapeLayer->GetInputSlot(0));
513 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
514 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
515 }
516 else
517 {
518 input.Connect(startLayer->GetInputSlot(0));
519 }
520
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100521 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100522 }
523 else
524 {
525 return Fail("%s: ProcessActivation failed", __func__);
526 }
527}
528
529bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
530 const Model& model,
531 ConversionData& data)
532{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100533 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100534 if (!input.IsValid())
535 {
536 return Fail("%s: Operation has invalid inputs", __func__);
537 }
538
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100539 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100540 if (!output)
541 {
542 return Fail("%s: Could not read output 0", __func__);
543 }
544
narpra012fb804a2018-10-22 14:52:32 +0100545 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
arovir01b0717b52018-09-05 17:03:25 +0100546 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
547
arovir01b0717b52018-09-05 17:03:25 +0100548 armnn::NormalizationDescriptor descriptor;
549
narpra012fb804a2018-10-22 14:52:32 +0100550 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +0100551 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
narpra012fb804a2018-10-22 14:52:32 +0100552 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
arovir01b0717b52018-09-05 17:03:25 +0100553
554 if (!input.IsValid() ||
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100555 !GetInputScalar<hal_1_0::HalPolicy>(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) ||
556 !GetInputFloat32<hal_1_0::HalPolicy>(operation, 2, descriptor.m_K, model, data) ||
557 !GetInputFloat32<hal_1_0::HalPolicy>(operation, 3, descriptor.m_Alpha, model, data) ||
558 !GetInputFloat32<hal_1_0::HalPolicy>(operation, 4, descriptor.m_Beta, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100559 {
560 return Fail("%s: Operation has invalid inputs", __func__);
561 }
562
563 // ArmNN expects normSize to be the full size of the normalization
564 // window rather than the radius as in AndroidNN.
565 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
566
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100567 if (!IsLayerSupportedForAnyBackend(__func__,
568 armnn::IsNormalizationSupported,
569 data.m_Backends,
570 inputInfo,
571 outputInfo,
572 descriptor))
arovir01b0717b52018-09-05 17:03:25 +0100573 {
574 return false;
575 }
576
577
578 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
579 assert(layer != nullptr);
narpra012fb804a2018-10-22 14:52:32 +0100580 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +0100581
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100582 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100583}
584
585bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
586{
587 armnn::ActivationDescriptor desc;
588 desc.m_Function = armnn::ActivationFunction::Sigmoid;
589
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100590 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100591}
592
593bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
594{
595 // Inputs:
596 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
597 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100598 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100599 if (!input.IsValid())
600 {
601 return Fail("%s: Could not read input 0: input", __func__);
602 }
603 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100604 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 18, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100605 if (!outputStateIn.IsValid())
606 {
607 return Fail("%s: Could not read input 18: outputStateIn", __func__);
608 }
609 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100610 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 19, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100611 if (!cellStateIn.IsValid())
612 {
613 return Fail("%s: Could not read input 19: cellStateIn", __func__);
614 }
615
616 // Get the mandatory input tensors:
617 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
618 // [num_units, input_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100619 const ConstTensorPin inputToForgetWeightsPin =
620 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 2, model, data);
621 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
622 // [num_units, input_size].
623 const ConstTensorPin inputToCellWeightsPin =
624 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 3, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100625 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
626 // [num_units, input_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100627 const ConstTensorPin inputToOutputWeightsPin =
628 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 4, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100629 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
630 // [num_units, output_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100631 const ConstTensorPin recurrentToForgetWeightsPin =
632 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 6, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100633 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
634 // [num_units, output_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100635 const ConstTensorPin recurrentToCellWeightsPin =
636 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 7, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100637 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
638 // [num_units, output_size].
639 const ConstTensorPin recurrentToOutputWeightsPin =
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100640 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 8, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100641 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100642 const ConstTensorPin forgetGateBiasPin =
643 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 13, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100644 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100645 const ConstTensorPin cellBiasPin =
646 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 14, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100647 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100648 const ConstTensorPin outputGateBiasPin =
649 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 15, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100650
651 if (!inputToForgetWeightsPin.IsValid() ||
652 !inputToCellWeightsPin.IsValid() ||
653 !inputToOutputWeightsPin.IsValid() ||
654 !recurrentToForgetWeightsPin.IsValid() ||
655 !recurrentToCellWeightsPin.IsValid() ||
656 !recurrentToOutputWeightsPin.IsValid() ||
657 !forgetGateBiasPin.IsValid() ||
658 !cellBiasPin.IsValid() ||
659 !outputGateBiasPin.IsValid())
660 {
661 return Fail("%s: Operation has invalid tensor inputs", __func__);
662 }
663
664 // Get the optional input tensors:
665 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
666 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100667 const ConstTensorPin inputToInputWeightsPin =
668 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
669 1,
670 model,
671 data,
672 g_DontPermute,
673 nullptr,
674 true);
675
arovir01b0717b52018-09-05 17:03:25 +0100676 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
677 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
678 // “num_units”), or the second dimension of the “projection_weights”, if defined.
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100679 const ConstTensorPin recurrentToInputWeightsPin =
680 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
681 5,
682 model,
683 data,
684 g_DontPermute,
685 nullptr,
686 true);
687
arovir01b0717b52018-09-05 17:03:25 +0100688 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100689 const ConstTensorPin cellToInputWeightsPin =
690 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
691 9,
692 model,
693 data,
694 g_DontPermute,
695 nullptr,
696 true);
697
arovir01b0717b52018-09-05 17:03:25 +0100698 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100699 const ConstTensorPin cellToForgetWeightsPin =
700 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
701 10,
702 model,
703 data,
704 g_DontPermute,
705 nullptr,
706 true);
707
arovir01b0717b52018-09-05 17:03:25 +0100708 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100709 const ConstTensorPin cellToOutputWeightsPin =
710 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
711 11,
712 model,
713 data,
714 g_DontPermute,
715 nullptr,
716 true);
717
arovir01b0717b52018-09-05 17:03:25 +0100718 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100719 const ConstTensorPin inputGateBiasPin =
720 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
721 12,
722 model,
723 data,
724 g_DontPermute,
725 nullptr,
726 true);
727
arovir01b0717b52018-09-05 17:03:25 +0100728 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
729 // [output_size, num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100730 const ConstTensorPin projectionWeightsPin =
731 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
732 16,
733 model,
734 data,
735 g_DontPermute,
736 nullptr,
737 true);
738
arovir01b0717b52018-09-05 17:03:25 +0100739 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100740 const ConstTensorPin projectionBiasPin =
741 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
742 17,
743 model,
744 data,
745 g_DontPermute,
746 nullptr,
747 true);
arovir01b0717b52018-09-05 17:03:25 +0100748
749 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
750 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
751 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
752 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
753 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
754 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
755 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
756 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
757 {
758 return Fail("%s: Operation has invalid tensor inputs", __func__);
759 }
760
761 // Get the mandatory input scalars (actually 1-D tensors of size 1):
762 // 20: The activation function: A value indicating the activation function:
763 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
764 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
765 // If set to 0.0 then clipping is disabled.
766 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
767 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
768 ActivationFn activation;
769 float cellClip;
770 float projClip;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100771 if (!GetInputActivationFunctionFromTensor<hal_1_0::HalPolicy>(operation, 20, activation, model, data) ||
772 !GetInputScalar<hal_1_0::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
773 !GetInputScalar<hal_1_0::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100774 {
775 return Fail("%s: Operation has invalid scalar inputs", __func__);
776 }
777
778 // Outputs:
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100779 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
780 // with CIFG, or [batch_size, num_units * 3] without CIFG.
781 const Operand* scratchBuffer = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100782 if (!scratchBuffer)
783 {
784 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
785 }
786 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100787 const Operand* outputStateOut = GetOutputOperand<hal_1_0::HalPolicy>(operation, 1, model);
arovir01b0717b52018-09-05 17:03:25 +0100788 if (!outputStateOut)
789 {
790 return Fail("%s: Could not read output 1: outputStateOut", __func__);
791 }
792 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100793 const Operand* cellStateOut = GetOutputOperand<hal_1_0::HalPolicy>(operation, 2, model);
arovir01b0717b52018-09-05 17:03:25 +0100794 if (!cellStateOut)
795 {
796 return Fail("%s: Could not read output 2: cellStateOut", __func__);
797 }
798 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
799 // effectively the same as the current “output state (out)” value.
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100800 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 3, model);
arovir01b0717b52018-09-05 17:03:25 +0100801 if (!output)
802 {
803 return Fail("%s: Could not read output 3: output", __func__);
804 }
805
806 // set the params structure for the AddLstmLayer call
807 armnn::LstmInputParams params;
808 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
809 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
810 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
811 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
812 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
813 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
814 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
815 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
816 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
817 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
818 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
819 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
820 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
821 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
822 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
823 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
824 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
825
826 // set the layer descriptor
827 armnn::LstmDescriptor desc;
828 desc.m_ActivationFunc = activation;
829 desc.m_ClippingThresCell = cellClip;
830 desc.m_ClippingThresProj = projClip;
831 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
832 params.m_RecurrentToInputWeights == nullptr ||
833 params.m_InputGateBias == nullptr);
834 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
835 params.m_CellToOutputWeights != nullptr);
836 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
837
838 // validate the optional input groups
839 if (desc.m_CifgEnabled &&
840 (params.m_InputToInputWeights != nullptr ||
841 params.m_RecurrentToInputWeights != nullptr ||
842 params.m_InputGateBias != nullptr))
843 {
844 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
845 " and input gate bias must be provided", __func__);
846 }
847
848 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
849 {
850 return Fail("%s: projection bias should not be provided without projection weights", __func__);
851 }
852
853 if (desc.m_PeepholeEnabled &&
854 (params.m_CellToForgetWeights == nullptr ||
855 params.m_CellToOutputWeights == nullptr ||
856 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
857 {
858 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
859 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
860 }
861
862 // Check if the layer is supported
863 // Inputs
864 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
865 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
866 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
867
868 // Outputs
869 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
870 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
871 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
872 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
873
874 // Basic parameters
875 const armnn::TensorInfo& inputToForgetWeights = params.m_InputToForgetWeights->GetInfo();
876 const armnn::TensorInfo& inputToCellWeights = params.m_InputToCellWeights->GetInfo();
877 const armnn::TensorInfo& inputToOutputWeights = params.m_InputToOutputWeights->GetInfo();
878 const armnn::TensorInfo& recurrentToForgetWeights = params.m_RecurrentToForgetWeights->GetInfo();
879 const armnn::TensorInfo& recurrentToCellWeights = params.m_RecurrentToCellWeights->GetInfo();
880 const armnn::TensorInfo& recurrentToOutputWeights = params.m_RecurrentToOutputWeights->GetInfo();
881 const armnn::TensorInfo& forgetGateBias = params.m_ForgetGateBias->GetInfo();
882 const armnn::TensorInfo& cellBias = params.m_CellBias->GetInfo();
883 const armnn::TensorInfo& outputGateBias = params.m_OutputGateBias->GetInfo();
884
885 //Optional parameters
886 const armnn::TensorInfo* inputToInputWeights = nullptr;
887 const armnn::TensorInfo* recurrentToInputWeights = nullptr;
888 const armnn::TensorInfo* cellToInputWeights = nullptr;
889 const armnn::TensorInfo* inputGateBias = nullptr;
890 const armnn::TensorInfo* projectionWeights = nullptr;
891 const armnn::TensorInfo* projectionBias = nullptr;
892 const armnn::TensorInfo* cellToForgetWeights = nullptr;
893 const armnn::TensorInfo* cellToOutputWeights = nullptr;
894
895 if(!desc.m_CifgEnabled)
896 {
897 inputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
898 recurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
899 if (params.m_CellToInputWeights != nullptr)
900 {
901 cellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
902 }
903 inputGateBias = &(params.m_InputGateBias->GetInfo());
904 }
905
906 if(desc.m_ProjectionEnabled)
907 {
908 projectionWeights = &(params.m_ProjectionWeights->GetInfo());
909 if (params.m_ProjectionBias != nullptr)
910 {
911 projectionBias = &(params.m_ProjectionBias->GetInfo());
912 }
913 }
914
915 if(desc.m_PeepholeEnabled)
916 {
917 cellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
918 cellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
919 }
920
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100921 if (!IsLayerSupportedForAnyBackend(__func__,
922 armnn::IsLstmSupported,
923 data.m_Backends,
924 inputInfo,
925 outputStateInInfo,
926 cellStateInInfo,
927 scratchBufferInfo,
928 outputStateOutInfo,
929 cellStateOutInfo,
930 outputInfo,
931 desc,
932 inputToForgetWeights,
933 inputToCellWeights,
934 inputToOutputWeights,
935 recurrentToForgetWeights,
936 recurrentToCellWeights,
937 recurrentToOutputWeights,
938 forgetGateBias,
939 cellBias,
940 outputGateBias,
941 inputToInputWeights,
942 recurrentToInputWeights,
943 cellToInputWeights,
944 inputGateBias,
945 projectionWeights,
946 projectionBias,
947 cellToForgetWeights,
948 cellToOutputWeights))
arovir01b0717b52018-09-05 17:03:25 +0100949 {
950 return false;
951 }
952
953 // Add the layer
954 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
955
956 input.Connect(layer->GetInputSlot(0));
957 outputStateIn.Connect(layer->GetInputSlot(1));
958 cellStateIn.Connect(layer->GetInputSlot(2));
959
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100960 return (SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, 0, model, data) &&
961 SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 1, *layer, 1, model, data) &&
962 SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 2, *layer, 2, model, data) &&
963 SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 3, *layer, 3, model, data));
arovir01b0717b52018-09-05 17:03:25 +0100964}
965
966bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
967{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100968 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100969 if (!input.IsValid())
970 {
971 return Fail("%s: Operation has invalid inputs", __func__);
972 }
973
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100974 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100975 if (!output)
976 {
977 return Fail("%s: Could not read output 0", __func__);
978 }
979
980 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
981 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
982
Matteo Martincigh58f71092018-09-25 15:58:52 +0100983 armnn::L2NormalizationDescriptor desc;
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +0100984 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matteo Martincigh58f71092018-09-25 15:58:52 +0100985
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100986 if (!IsLayerSupportedForAnyBackend(__func__,
987 armnn::IsL2NormalizationSupported,
988 data.m_Backends,
989 inputInfo,
990 outputInfo,
991 desc))
arovir01b0717b52018-09-05 17:03:25 +0100992 {
993 return false;
994 }
995
Matteo Martincigh58f71092018-09-25 15:58:52 +0100996 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer(desc);
arovir01b0717b52018-09-05 17:03:25 +0100997 assert(layer != nullptr);
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +0100998 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +0100999
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001000 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001001}
1002
1003bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
1004{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001005 return ConvertPooling2d<hal_1_0::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001006}
1007
1008bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
1009{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001010 return ConvertPooling2d<hal_1_0::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001011}
1012
1013bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1014{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001015 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
1016 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 1, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001017
1018 if (!input0.IsValid() || !input1.IsValid())
1019 {
1020 return Fail("%s: Operation has invalid inputs", __func__);
1021 }
1022
1023 // The FuseActivation parameter is always the input index 2
1024 // and it should be optional
1025 ActivationFn activationFunction;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001026 if (!GetOptionalInputActivation<hal_1_0::HalPolicy>(operation, 2, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001027 {
1028 return Fail("%s: Operation has invalid inputs", __func__);
1029 }
1030
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001031 const Operand* outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001032
1033 if (outputOperand == nullptr)
1034 {
1035 return false;
1036 }
1037
1038 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
1039
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001040 if (!IsLayerSupportedForAnyBackend(__func__,
1041 armnn::IsMultiplicationSupported,
1042 data.m_Backends,
1043 input0.GetTensorInfo(),
1044 input1.GetTensorInfo(),
1045 outInfo))
arovir01b0717b52018-09-05 17:03:25 +01001046 {
1047 return false;
1048 }
1049
1050 armnn::IConnectableLayer* const startLayer = data.m_Network->AddMultiplicationLayer();
1051 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
1052
1053 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
1054 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
1055
1056 if (endLayer != nullptr)
1057 {
1058 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001059 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001060 }
1061 else
1062 {
1063 return Fail("%s: ProcessActivation failed", __func__);
1064 }
1065}
1066
1067bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1068{
1069 armnn::ActivationDescriptor desc;
1070 desc.m_Function = armnn::ActivationFunction::ReLu;
1071
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001072 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001073}
1074
1075bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1076{
1077 armnn::ActivationDescriptor desc;
1078 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1079 desc.m_A = 1.0f;
1080 desc.m_B = -1.0f;
1081
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001082 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001083}
1084
1085bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1086{
1087 armnn::ActivationDescriptor desc;
1088 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1089 desc.m_A = 6.0f;
1090
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001091 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001092}
1093
1094bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1095{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001096 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001097 if (!input.IsValid())
1098 {
1099 return Fail("%s: Operation has invalid inputs", __func__);
1100 }
1101
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001102 const Operand* outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001103 if (!outputOperand)
1104 {
1105 return Fail("%s: Operation has no outputs", __func__);
1106 }
1107
1108 const armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
1109
1110 armnn::SoftmaxDescriptor desc;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001111 if (!GetInputFloat32<hal_1_0::HalPolicy>(operation, 1, desc.m_Beta, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001112 {
1113 return Fail("%s: Operation has invalid inputs", __func__);
1114 }
1115
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001116 if (!IsLayerSupportedForAnyBackend(__func__,
1117 armnn::IsSoftmaxSupported,
1118 data.m_Backends,
1119 input.GetTensorInfo(),
1120 outInfo,
1121 desc))
arovir01b0717b52018-09-05 17:03:25 +01001122 {
1123 return false;
1124 }
1125
1126 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1127 assert(layer != nullptr);
1128 input.Connect(layer->GetInputSlot(0));
1129
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001130 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001131}
1132
1133bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1134{
1135 armnn::ActivationDescriptor desc;
1136 desc.m_Function = armnn::ActivationFunction::TanH;
1137 desc.m_A = 1.0f; // android nn does not support tanH parameters
1138 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1139
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001140 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001141}
1142
1143bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1144{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001145 const Operand* inputOperand = GetInputOperand<hal_1_0::HalPolicy>(operation, 0, model);
1146 const Operand* requestedShapeOperand = GetInputOperand<hal_1_0::HalPolicy>(operation, 1, model);
1147 const Operand* outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001148
1149 if (inputOperand == nullptr
1150 || requestedShapeOperand == nullptr
1151 || outputOperand == nullptr)
1152 {
1153 return Fail("%s: Operation has invalid inputs", __func__);
1154 }
1155
1156
1157 if (requestedShapeOperand->dimensions.size() != 1)
1158 {
1159 return Fail("%s: Input 1 expected to be one-dimensional (found %i dimensions)",
1160 __func__, requestedShapeOperand->dimensions.size());
1161 }
1162
1163 std::vector<int32_t> targetDimensions;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001164 if (!GetTensorInt32Values<hal_1_0::HalPolicy>(*requestedShapeOperand, targetDimensions, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001165 {
1166 return Fail("%s: Could not read values of input 1", __func__);
1167 }
1168
1169 const Shape inputOperandShape = GetOperandShape(*inputOperand);
1170
1171 Shape requestedShape;
1172 // targetDimensions may contain special values (e.g. -1). reshapePrepare() is an AndroidNN provided utility
1173 // function that resolves these values into a fully specified tensor shape.
1174 if (!reshapePrepare(inputOperandShape, targetDimensions.data(), targetDimensions.size(), &requestedShape))
1175 {
1176 return Fail("%s: Failed to resolve the requested shape", __func__);
1177 }
1178
1179 const Shape outputOperandShape = GetOperandShape(*outputOperand);
1180 if (!SameShape(requestedShape, outputOperandShape))
1181 {
1182 return Fail("%s: Shape of output operand does not match resolved requested shape", __func__);
1183 }
1184
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001185 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001186 if (!input.IsValid())
1187 {
1188 return Fail("%s: Could not read input 0", __func__);
1189 }
1190
arovir01b0717b52018-09-05 17:03:25 +01001191 armnn::ReshapeDescriptor reshapeDescriptor;
1192 reshapeDescriptor.m_TargetShape = armnn::TensorShape(requestedShape.dimensions.size(),
1193 requestedShape.dimensions.data());
1194
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001195 if (!IsLayerSupportedForAnyBackend(__func__,
1196 armnn::IsReshapeSupported,
1197 data.m_Backends,
1198 input.GetTensorInfo(),
1199 reshapeDescriptor))
Matteo Martincigh265d1ad2019-01-08 18:14:53 +00001200 {
1201 return false;
1202 }
1203
arovir01b0717b52018-09-05 17:03:25 +01001204 armnn::IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
1205 assert(layer != nullptr);
1206 input.Connect(layer->GetInputSlot(0));
1207
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001208 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001209}
1210
1211bool HalPolicy::ConvertResizeBilinear(const Operation& operation, const Model& model, ConversionData& data)
1212{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001213 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001214 if (!input.IsValid())
1215 {
1216 return Fail("%s: Could not read input 0", __func__);
1217 }
1218
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001219 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001220 if (!output)
1221 {
1222 return Fail("%s: Could not read output 0", __func__);
1223 }
1224
1225 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1226 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1227
Aron Virginas-Tara5daf862019-07-01 19:07:20 +01001228 armnn::ResizeDescriptor desc;
1229 desc.m_Method = armnn::ResizeMethod::Bilinear;
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001230 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001231
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001232 if (!IsLayerSupportedForAnyBackend(__func__,
Aron Virginas-Tara5daf862019-07-01 19:07:20 +01001233 armnn::IsResizeSupported,
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001234 data.m_Backends,
1235 inputInfo,
Aron Virginas-Tara5daf862019-07-01 19:07:20 +01001236 outputInfo,
1237 desc))
arovir01b0717b52018-09-05 17:03:25 +01001238 {
1239 return false;
1240 }
1241
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001242 if (!GetInputScalar<hal_1_0::HalPolicy>(operation, 1, OperandType::INT32, desc.m_TargetHeight, model, data) ||
1243 !GetInputScalar<hal_1_0::HalPolicy>(operation, 2, OperandType::INT32, desc.m_TargetWidth, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001244 {
1245 return Fail("%s: Operation has invalid inputs", __func__);
1246 }
1247
Aron Virginas-Tara5daf862019-07-01 19:07:20 +01001248 armnn::IConnectableLayer* layer = data.m_Network->AddResizeLayer(desc);
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001249
arovir01b0717b52018-09-05 17:03:25 +01001250 assert(layer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +01001251
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001252 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1253 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +01001254
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001255 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001256
1257}
1258
1259} // namespace hal_1_0
Matteo Martincigh58f71092018-09-25 15:58:52 +01001260} // namespace armnn_driver