blob: 2149d40f0f714846b5384327e56211e15c3ab224 [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"
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +010011#include "OutputShapeUtils.hpp"
arovir015602b192018-10-04 16:15:02 +010012
arovir01b0717b52018-09-05 17:03:25 +010013namespace armnn_driver
14{
15namespace hal_1_0
16{
17
18bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
19{
20 switch (operation.type)
21 {
22 case V1_0::OperationType::ADD:
23 return ConvertAdd(operation, model, data);
24 case V1_0::OperationType::AVERAGE_POOL_2D:
25 return ConvertAveragePool2d(operation, model, data);
26 case V1_0::OperationType::CONCATENATION:
27 return ConvertConcatenation(operation, model, data);
28 case V1_0::OperationType::CONV_2D:
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +010029 return ValidateConv2dParameters(operation) &&
30 ConvertConv2d<hal_1_0::HalPolicy>(operation, model, data);
arovir01b0717b52018-09-05 17:03:25 +010031 case V1_0::OperationType::DEPTHWISE_CONV_2D:
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +010032 return ValidateDepthwiseConv2dParameters(operation) &&
33 ConvertDepthwiseConv2d<hal_1_0::HalPolicy>(operation, model, data);
David Monahanacf479a2019-05-29 14:27:04 +010034 case V1_0::OperationType::DEQUANTIZE:
35 return ConvertDequantize(operation, model, data);
arovir01b0717b52018-09-05 17:03:25 +010036 case V1_0::OperationType::FLOOR:
37 return ConvertFloor(operation, model, data);
38 case V1_0::OperationType::FULLY_CONNECTED:
39 return ConvertFullyConnected(operation, model, data);
40 case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
41 return ConvertLocalResponseNormalization(operation, model, data);
42 case V1_0::OperationType::LOGISTIC:
43 return ConvertLogistic(operation, model, data);
44 case V1_0::OperationType::LSTM:
45 return ConvertLstm(operation, model, data);
46 case V1_0::OperationType::L2_NORMALIZATION:
47 return ConvertL2Normalization(operation, model, data);
48 case V1_0::OperationType::L2_POOL_2D:
49 return ConvertL2Pool2d(operation, model, data);
50 case V1_0::OperationType::MAX_POOL_2D:
51 return ConvertMaxPool2d(operation, model, data);
52 case V1_0::OperationType::MUL:
53 return ConvertMul(operation, model, data);
54 case V1_0::OperationType::RELU:
55 return ConvertReLu(operation, model, data);
56 case V1_0::OperationType::RELU1:
57 return ConvertReLu1(operation, model, data);
58 case V1_0::OperationType::RELU6:
59 return ConvertReLu6(operation, model, data);
60 case V1_0::OperationType::SOFTMAX:
61 return ConvertSoftmax(operation, model, data);
Keith Davisa6bc52f2019-06-26 09:39:49 +010062 case V1_0::OperationType::SPACE_TO_DEPTH:
63 return ConvertSpaceToDepth(operation, model, data);
arovir01b0717b52018-09-05 17:03:25 +010064 case V1_0::OperationType::TANH:
65 return ConvertTanH(operation, model, data);
66 case V1_0::OperationType::RESHAPE:
67 return ConvertReshape(operation, model, data);
68 case V1_0::OperationType::RESIZE_BILINEAR:
69 return ConvertResizeBilinear(operation, model, data);
70 default:
71 return Fail("%s: Operation type %s not supported in ArmnnDriver",
72 __func__, toString(operation.type).c_str());
73 }
74}
75
Mike Kellyb5fdf382019-06-11 16:35:25 +010076bool HalPolicy::ValidateConv2dParameters(const Operation &operation)
77{
78 if (operation.inputs.size() != 10 && operation.inputs.size() != 7)
79 {
80 return Fail("%s: Unsupported number of operation inputs", __func__);
81 }
82 return true;
83}
84
85bool HalPolicy::ValidateDepthwiseConv2dParameters(const Operation &operation)
86{
87 if (operation.inputs.size() != 11 && operation.inputs.size() != 8)
88 {
89 return Fail("%s: Unsupported number of operation inputs", __func__);
90 }
91 return true;
92}
93
arovir01b0717b52018-09-05 17:03:25 +010094bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
95{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +010096 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
97 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 1, model, data);
arovir01b0717b52018-09-05 17:03:25 +010098
99 if (!input0.IsValid() || !input1.IsValid())
100 {
101 return Fail("%s: Operation has invalid inputs", __func__);
102 }
103
104 // The FuseActivation parameter is always the input index 2
105 // and it should be optional
106 ActivationFn activationFunction;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100107 if (!GetOptionalInputActivation<hal_1_0::HalPolicy>(operation, 2, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100108 {
109 return Fail("%s: Operation has invalid inputs", __func__);
110 }
111
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100112 const Operand* outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100113 if (!outputOperand)
114 {
115 return false;
116 }
117
118 const armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
119
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100120 if (!IsLayerSupportedForAnyBackend(__func__,
121 armnn::IsAdditionSupported,
122 data.m_Backends,
123 input0.GetTensorInfo(),
124 input1.GetTensorInfo(),
125 outInfo))
arovir01b0717b52018-09-05 17:03:25 +0100126 {
127 return false;
128 }
129
130 armnn::IConnectableLayer* const startLayer = data.m_Network->AddAdditionLayer();
131 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
132
133 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
134 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
135
136 if (endLayer != nullptr)
137 {
138 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100139 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100140 }
141 else
142 {
143 return Fail("%s: ProcessActivation failed", __func__);
144 }
145}
146
147bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data)
148{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100149 return ConvertPooling2d<hal_1_0::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Average, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100150}
151
152bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
153{
154 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
155 if (operation.inputs.size() <= 1)
156 {
157 return Fail("%s: Operation has insufficient arguments", __func__);
158 }
159
160 // Get inputs and outputs
161 const std::size_t numInputTensors = operation.inputs.size() - 1;
162
163 int32_t concatDim;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100164 if (!GetInputScalar<hal_1_0::HalPolicy>(operation, numInputTensors, OperandType::INT32, concatDim, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100165 {
166 return Fail("%s: Operation has invalid inputs", __func__);
167 }
168
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100169 const Operand* const outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100170 if (!outputOperand)
171 {
172 return Fail("%s: Operation has no outputs", __func__);
173 }
174
175
176 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
177 armnn::TensorShape outputShape = outputInfo.GetShape();
178
179 //
180 // handle negative concat dims along the lines of tensorflow as described here:
181 // https://www.tensorflow.org/api_docs/python/tf/concat
182 // "negative axis refers to axis + rank(values)-th dimension"
183 //
184 if (concatDim < 0)
185 {
186 concatDim += outputShape.GetNumDimensions();
187 }
188
189 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
190 {
191 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
192 }
193
194 std::vector<LayerInputHandle> inputHandles;
195 std::vector<armnn::TensorShape> inputShapes;
196
197 inputHandles.reserve(numInputTensors);
198 inputShapes.reserve(numInputTensors);
199
200 bool inputsHaveBeenReshaped = false;
201 unsigned int tensorDimensionsAdded = 0;
202
203 for (uint32_t i = 0; i < numInputTensors; ++i)
204 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100205 const Operand* const operand = GetInputOperand<hal_1_0::HalPolicy>(operation, i, model);
arovir01b0717b52018-09-05 17:03:25 +0100206 if (!operand)
207 {
208 return Fail("%s: Operation has invalid inputs", __func__);
209 }
210
211 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100212 LayerInputHandle operandInputHandle =
213 ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, i, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100214
215 if (operandShape.GetNumDimensions() == 0)
216 {
217 return Fail("%s: Operands with rank 0 are not supported", __func__);
218 }
219
220 if (RequiresReshape(operandShape))
221 {
222 inputsHaveBeenReshaped = true;
223
224 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
225
226 // Expand the tensor to three dimensions
227 if (operandShape.GetNumDimensions() == 2)
228 {
229 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
230 tensorDimensionsAdded = 1;
231 }
232 else
233 {
234 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
235 tensorDimensionsAdded = 2;
236 }
237
238 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
239 *data.m_Network,
240 operandInputHandle,
241 reshapeInfo
242 );
243
244 // Point to the reshape operation rather then the input operation
245 operandShape = reshapeInfo.GetShape();
246 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
247 }
248
249 inputShapes.emplace_back(operandShape);
250 inputHandles.emplace_back(operandInputHandle);
251
252 if (!inputHandles.back().IsValid())
253 {
254 return Fail("%s: Operation has invalid inputs", __func__);
255 }
256 }
257
258 BOOST_ASSERT(inputShapes.size() == inputHandles.size());
259
260 if (inputsHaveBeenReshaped)
261 {
262 // Adjust the concatenation dimension by the amount of dimensions added (if any)
263 concatDim += tensorDimensionsAdded;
264
265 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
266 if (tensorDimensionsAdded == 1)
267 {
268 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
269 }
270 else if (tensorDimensionsAdded == 2)
271 {
narpra01f176d5a2018-11-18 20:17:48 +0000272 outputShape = armnn::TensorShape({1, 1, outputShape[0]});
arovir01b0717b52018-09-05 17:03:25 +0100273 }
274 }
275
narpra01f176d5a2018-11-18 20:17:48 +0000276 // Check if permutations is required and get the pair of permutations required for the concatenation.
277 // 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 +0100278 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
279 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
280
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100281 bool needPermute =
282 CreateConcatPermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
arovir01b0717b52018-09-05 17:03:25 +0100283
narpra01f176d5a2018-11-18 20:17:48 +0000284 if (needPermute)
285 {
286 outputShape = armnnUtils::Permuted(outputShape, permutationPair.first);
287 }
288
arovir01b0717b52018-09-05 17:03:25 +0100289 outputInfo.SetShape(outputShape);
290
291 // this is no-op for identity swizzles, otherwise it replaces both
292 // the handles and shapes with the swizzled layer output handles and shapes
293 SwizzleInputs(*data.m_Network, inputHandles, inputShapes, permutationPair.first);
294
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100295 // Create an armnn concat layer descriptor - this will also perform validation on the input shapes
296 armnn::OriginsDescriptor concatDescriptor;
narpra01f176d5a2018-11-18 20:17:48 +0000297
arovir01b0717b52018-09-05 17:03:25 +0100298 try
299 {
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100300 // The concat descriptor is always created across the only supported concat dimension
narpra01f176d5a2018-11-18 20:17:48 +0000301 // 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 +0100302 concatDescriptor =
Jim Flynn52aa9352019-05-20 12:52:30 +0100303 armnn::CreateDescriptorForConcatenation(inputShapes.begin(), inputShapes.end(), concatDim);
arovir01b0717b52018-09-05 17:03:25 +0100304 }
305 catch (const armnn::Exception& error)
306 {
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100307 return Fail("%s: Error preparing concat descriptor. %s", __func__, error.what());
arovir01b0717b52018-09-05 17:03:25 +0100308 }
309
310 // Validate the output shape is correct given the input shapes based on the
narpra01f176d5a2018-11-18 20:17:48 +0000311 // 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 +0100312 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
313 {
314 return Fail("%s: Error validating the output shape for concat", __func__);
315 }
316
317 std::vector<const armnn::TensorInfo*> inputTensorInfos;
318 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
319 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100320 if (!IsLayerSupportedForAnyBackend(__func__,
Jim Flynn073d7a32019-05-13 13:52:56 +0100321 armnn::IsConcatSupported,
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100322 data.m_Backends,
323 inputTensorInfos,
324 outputInfo,
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100325 concatDescriptor))
arovir01b0717b52018-09-05 17:03:25 +0100326 {
327 return false;
328 }
329
Jim Flynn7b1e41f2019-05-22 18:00:04 +0100330 armnn::IConnectableLayer* layer = data.m_Network->AddConcatLayer(concatDescriptor);
arovir01b0717b52018-09-05 17:03:25 +0100331 assert(layer != nullptr);
332 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
333
334 // Connect inputs to the layer
335 const int numInputSlots = layer->GetNumInputSlots();
336 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
337 for (int i = 0; i < numInputSlots; ++i)
338 {
339 // connect the input directly to the merge (concat) layer
340 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
341 }
342
narpra01f176d5a2018-11-18 20:17:48 +0000343 if (needPermute)
344 {
345 // Add permutation layer and connect the output to it, the permutation becomes the output layer
346 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(*data.m_Network,
347 layer->GetOutputSlot(0),
348 permutationPair.second);
349 layer = &deswizzleLayer;
350 }
arovir01b0717b52018-09-05 17:03:25 +0100351
352 if (inputsHaveBeenReshaped)
353 {
354 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
355
356 // Undo the reshape knowing the amount of dimensions added
357 if (tensorDimensionsAdded == 1)
358 {
359 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
360 afterConcatInfo.GetShape()[2] }));
361 }
362 else if (tensorDimensionsAdded == 2)
363 {
narpra01f176d5a2018-11-18 20:17:48 +0000364 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2] }));
arovir01b0717b52018-09-05 17:03:25 +0100365 }
366
367 layer = &AddReshapeLayer(
368 *data.m_Network,
369 layer->GetOutputSlot(0),
370 afterConcatInfo
371 );
372 }
373
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100374 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100375}
376
David Monahanacf479a2019-05-29 14:27:04 +0100377bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
378{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100379 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
David Monahanacf479a2019-05-29 14:27:04 +0100380
381 if (!input.IsValid())
382 {
383 return Fail("%s: Operation has invalid input", __func__);
384 }
385
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100386 const Operand* const outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
David Monahanacf479a2019-05-29 14:27:04 +0100387 if (!outputOperand)
388 {
389 return Fail("%s: Operation has invalid outputs", __func__);
390 }
391
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100392 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
393 if (IsDynamicOutput(outputInfo))
394 {
395 return Fail("%s: Dynamic output not supported", __func__);
396 }
397
David Monahanacf479a2019-05-29 14:27:04 +0100398 if (!IsLayerSupportedForAnyBackend(__func__,
399 armnn::IsDequantizeSupported,
400 data.m_Backends,
401 input.GetTensorInfo(),
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100402 outputInfo))
David Monahanacf479a2019-05-29 14:27:04 +0100403 {
404 return false;
405 }
406
407 armnn::IConnectableLayer* const layer = data.m_Network->AddDequantizeLayer();
408 assert(layer != nullptr);
409 input.Connect(layer->GetInputSlot(0));
410
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100411 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
David Monahanacf479a2019-05-29 14:27:04 +0100412}
413
arovir01b0717b52018-09-05 17:03:25 +0100414bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
415{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100416 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100417 if (!input.IsValid())
418 {
419 return Fail("%s: Operation has invalid inputs", __func__);
420 }
421
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100422 const Operand* const outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100423 if (!outputOperand)
424 {
425 return Fail("%s: Operation has invalid outputs", __func__);
426 }
427
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100428 if (!IsLayerSupportedForAnyBackend(__func__,
429 armnn::IsFloorSupported,
430 data.m_Backends,
431 input.GetTensorInfo(),
432 GetTensorInfoForOperand(*outputOperand)))
arovir01b0717b52018-09-05 17:03:25 +0100433 {
434 return false;
435 }
436
437 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
438 assert(layer != nullptr);
439 input.Connect(layer->GetInputSlot(0));
440
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100441 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100442}
443
444bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
445{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100446 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100447 if (!input.IsValid())
448 {
449 return Fail("%s: Operation has invalid inputs", __func__);
450 }
451
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100452 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100453 if (!output)
454 {
455 return Fail("%s: Could not read output 0", __func__);
456 }
457
458 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
459 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
460
461 // ArmNN does not currently support non-fixed weights or bias
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100462 ConstTensorPin weightsPin =
463 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 1, model, data); // 2D
464 ConstTensorPin biasPin =
465 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 2, model, data); // 1D
arovir01b0717b52018-09-05 17:03:25 +0100466
467 if (!weightsPin.IsValid() || !biasPin.IsValid())
468 {
469 return Fail("%s: Operation has invalid inputs", __func__);
470 }
471
472 armnn::ConstTensor weights = weightsPin.GetConstTensor();
473 armnn::ConstTensor bias = biasPin.GetConstTensor();
arovir01b0717b52018-09-05 17:03:25 +0100474 armnn::TensorInfo reshapedInfo = inputInfo;
Matthew Benthamf61c2702019-04-23 16:43:27 +0100475
476 try
arovir01b0717b52018-09-05 17:03:25 +0100477 {
Matthew Benthamf61c2702019-04-23 16:43:27 +0100478 reshapedInfo.SetShape(FlattenFullyConnectedInput(inputInfo.GetShape(), weights.GetInfo().GetShape()));
479 } catch (const std::exception &e) {
480 return Fail("%s: %s", __func__, e.what());
arovir01b0717b52018-09-05 17:03:25 +0100481 }
482
483 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
484 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
485
486 ActivationFn activationFunction;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100487 if (!GetInputActivationFunction<hal_1_0::HalPolicy>(operation, 3, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100488 {
489 return Fail("%s: Operation has invalid inputs", __func__);
490 }
491
492 armnn::FullyConnectedDescriptor desc;
493 desc.m_TransposeWeightMatrix = true;
494 desc.m_BiasEnabled = true;
495
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100496 if (!IsLayerSupportedForAnyBackend(__func__,
497 armnn::IsFullyConnectedSupported,
498 data.m_Backends,
499 reshapedInfo,
500 outputInfo,
501 weights.GetInfo(),
502 bias.GetInfo(),
503 desc))
arovir01b0717b52018-09-05 17:03:25 +0100504 {
505 return false;
506 }
507
Matteo Martincighba01f372019-05-14 13:28:21 +0100508 armnn::IConnectableLayer* startLayer =
509 data.m_Network->AddFullyConnectedLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
arovir01b0717b52018-09-05 17:03:25 +0100510 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
511
512 if (endLayer != nullptr)
513 {
514 if (inputInfo.GetNumDimensions() > 2U)
515 {
516 armnn::ReshapeDescriptor reshapeDescriptor;
517 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
518
519 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
520 assert(reshapeLayer != nullptr);
521 input.Connect(reshapeLayer->GetInputSlot(0));
522 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
523 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
524 }
525 else
526 {
527 input.Connect(startLayer->GetInputSlot(0));
528 }
529
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100530 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100531 }
532 else
533 {
534 return Fail("%s: ProcessActivation failed", __func__);
535 }
536}
537
538bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation,
539 const Model& model,
540 ConversionData& data)
541{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100542 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100543 if (!input.IsValid())
544 {
545 return Fail("%s: Operation has invalid inputs", __func__);
546 }
547
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100548 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100549 if (!output)
550 {
551 return Fail("%s: Could not read output 0", __func__);
552 }
553
narpra012fb804a2018-10-22 14:52:32 +0100554 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
arovir01b0717b52018-09-05 17:03:25 +0100555 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
556
arovir01b0717b52018-09-05 17:03:25 +0100557 armnn::NormalizationDescriptor descriptor;
558
narpra012fb804a2018-10-22 14:52:32 +0100559 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +0100560 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
narpra012fb804a2018-10-22 14:52:32 +0100561 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
arovir01b0717b52018-09-05 17:03:25 +0100562
563 if (!input.IsValid() ||
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100564 !GetInputScalar<hal_1_0::HalPolicy>(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) ||
565 !GetInputFloat32<hal_1_0::HalPolicy>(operation, 2, descriptor.m_K, model, data) ||
566 !GetInputFloat32<hal_1_0::HalPolicy>(operation, 3, descriptor.m_Alpha, model, data) ||
567 !GetInputFloat32<hal_1_0::HalPolicy>(operation, 4, descriptor.m_Beta, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100568 {
569 return Fail("%s: Operation has invalid inputs", __func__);
570 }
571
572 // ArmNN expects normSize to be the full size of the normalization
573 // window rather than the radius as in AndroidNN.
574 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
575
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100576 if (!IsLayerSupportedForAnyBackend(__func__,
577 armnn::IsNormalizationSupported,
578 data.m_Backends,
579 inputInfo,
580 outputInfo,
581 descriptor))
arovir01b0717b52018-09-05 17:03:25 +0100582 {
583 return false;
584 }
585
586
587 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
588 assert(layer != nullptr);
narpra012fb804a2018-10-22 14:52:32 +0100589 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +0100590
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100591 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100592}
593
594bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
595{
596 armnn::ActivationDescriptor desc;
597 desc.m_Function = armnn::ActivationFunction::Sigmoid;
598
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100599 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100600}
601
602bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data)
603{
604 // Inputs:
605 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
606 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100607 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100608 if (!input.IsValid())
609 {
610 return Fail("%s: Could not read input 0: input", __func__);
611 }
612 // 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 +0100613 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 18, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100614 if (!outputStateIn.IsValid())
615 {
616 return Fail("%s: Could not read input 18: outputStateIn", __func__);
617 }
618 // 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 +0100619 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 19, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100620 if (!cellStateIn.IsValid())
621 {
622 return Fail("%s: Could not read input 19: cellStateIn", __func__);
623 }
624
625 // Get the mandatory input tensors:
626 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
627 // [num_units, input_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100628 const ConstTensorPin inputToForgetWeightsPin =
629 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 2, model, data);
630 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
631 // [num_units, input_size].
632 const ConstTensorPin inputToCellWeightsPin =
633 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 3, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100634 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
635 // [num_units, input_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100636 const ConstTensorPin inputToOutputWeightsPin =
637 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 4, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100638 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
639 // [num_units, output_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100640 const ConstTensorPin recurrentToForgetWeightsPin =
641 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 6, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100642 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
643 // [num_units, output_size].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100644 const ConstTensorPin recurrentToCellWeightsPin =
645 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 7, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100646 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
647 // [num_units, output_size].
648 const ConstTensorPin recurrentToOutputWeightsPin =
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100649 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 8, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100650 // 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 +0100651 const ConstTensorPin forgetGateBiasPin =
652 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 13, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100653 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100654 const ConstTensorPin cellBiasPin =
655 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 14, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100656 // 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 +0100657 const ConstTensorPin outputGateBiasPin =
658 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation, 15, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100659
660 if (!inputToForgetWeightsPin.IsValid() ||
661 !inputToCellWeightsPin.IsValid() ||
662 !inputToOutputWeightsPin.IsValid() ||
663 !recurrentToForgetWeightsPin.IsValid() ||
664 !recurrentToCellWeightsPin.IsValid() ||
665 !recurrentToOutputWeightsPin.IsValid() ||
666 !forgetGateBiasPin.IsValid() ||
667 !cellBiasPin.IsValid() ||
668 !outputGateBiasPin.IsValid())
669 {
670 return Fail("%s: Operation has invalid tensor inputs", __func__);
671 }
672
673 // Get the optional input tensors:
674 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
675 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100676 const ConstTensorPin inputToInputWeightsPin =
677 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
678 1,
679 model,
680 data,
681 g_DontPermute,
682 nullptr,
683 true);
684
arovir01b0717b52018-09-05 17:03:25 +0100685 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
686 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
687 // “num_units”), or the second dimension of the “projection_weights”, if defined.
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100688 const ConstTensorPin recurrentToInputWeightsPin =
689 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
690 5,
691 model,
692 data,
693 g_DontPermute,
694 nullptr,
695 true);
696
arovir01b0717b52018-09-05 17:03:25 +0100697 // 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 +0100698 const ConstTensorPin cellToInputWeightsPin =
699 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
700 9,
701 model,
702 data,
703 g_DontPermute,
704 nullptr,
705 true);
706
arovir01b0717b52018-09-05 17:03:25 +0100707 // 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 +0100708 const ConstTensorPin cellToForgetWeightsPin =
709 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
710 10,
711 model,
712 data,
713 g_DontPermute,
714 nullptr,
715 true);
716
arovir01b0717b52018-09-05 17:03:25 +0100717 // 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 +0100718 const ConstTensorPin cellToOutputWeightsPin =
719 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
720 11,
721 model,
722 data,
723 g_DontPermute,
724 nullptr,
725 true);
726
arovir01b0717b52018-09-05 17:03:25 +0100727 // 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 +0100728 const ConstTensorPin inputGateBiasPin =
729 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
730 12,
731 model,
732 data,
733 g_DontPermute,
734 nullptr,
735 true);
736
arovir01b0717b52018-09-05 17:03:25 +0100737 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
738 // [output_size, num_units].
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100739 const ConstTensorPin projectionWeightsPin =
740 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
741 16,
742 model,
743 data,
744 g_DontPermute,
745 nullptr,
746 true);
747
arovir01b0717b52018-09-05 17:03:25 +0100748 // 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 +0100749 const ConstTensorPin projectionBiasPin =
750 ConvertOperationInputToConstTensorPin<hal_1_0::HalPolicy>(operation,
751 17,
752 model,
753 data,
754 g_DontPermute,
755 nullptr,
756 true);
arovir01b0717b52018-09-05 17:03:25 +0100757
758 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
759 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
760 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
761 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
762 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
763 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
764 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
765 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
766 {
767 return Fail("%s: Operation has invalid tensor inputs", __func__);
768 }
769
770 // Get the mandatory input scalars (actually 1-D tensors of size 1):
771 // 20: The activation function: A value indicating the activation function:
772 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
773 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
774 // If set to 0.0 then clipping is disabled.
775 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
776 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
777 ActivationFn activation;
778 float cellClip;
779 float projClip;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100780 if (!GetInputActivationFunctionFromTensor<hal_1_0::HalPolicy>(operation, 20, activation, model, data) ||
781 !GetInputScalar<hal_1_0::HalPolicy>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
782 !GetInputScalar<hal_1_0::HalPolicy>(operation, 22, OperandType::FLOAT32, projClip, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100783 {
784 return Fail("%s: Operation has invalid scalar inputs", __func__);
785 }
786
787 // Outputs:
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100788 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
789 // with CIFG, or [batch_size, num_units * 3] without CIFG.
790 const Operand* scratchBuffer = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100791 if (!scratchBuffer)
792 {
793 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
794 }
795 // 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 +0100796 const Operand* outputStateOut = GetOutputOperand<hal_1_0::HalPolicy>(operation, 1, model);
arovir01b0717b52018-09-05 17:03:25 +0100797 if (!outputStateOut)
798 {
799 return Fail("%s: Could not read output 1: outputStateOut", __func__);
800 }
801 // 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 +0100802 const Operand* cellStateOut = GetOutputOperand<hal_1_0::HalPolicy>(operation, 2, model);
arovir01b0717b52018-09-05 17:03:25 +0100803 if (!cellStateOut)
804 {
805 return Fail("%s: Could not read output 2: cellStateOut", __func__);
806 }
807 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
808 // effectively the same as the current “output state (out)” value.
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100809 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 3, model);
arovir01b0717b52018-09-05 17:03:25 +0100810 if (!output)
811 {
812 return Fail("%s: Could not read output 3: output", __func__);
813 }
814
815 // set the params structure for the AddLstmLayer call
816 armnn::LstmInputParams params;
817 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
818 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
819 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
820 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
821 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
822 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
823 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
824 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
825 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
826 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
827 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
828 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
829 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
830 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
831 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
832 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
833 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
834
835 // set the layer descriptor
836 armnn::LstmDescriptor desc;
837 desc.m_ActivationFunc = activation;
838 desc.m_ClippingThresCell = cellClip;
839 desc.m_ClippingThresProj = projClip;
840 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
841 params.m_RecurrentToInputWeights == nullptr ||
842 params.m_InputGateBias == nullptr);
843 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
844 params.m_CellToOutputWeights != nullptr);
845 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
846
847 // validate the optional input groups
848 if (desc.m_CifgEnabled &&
849 (params.m_InputToInputWeights != nullptr ||
850 params.m_RecurrentToInputWeights != nullptr ||
851 params.m_InputGateBias != nullptr))
852 {
853 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
854 " and input gate bias must be provided", __func__);
855 }
856
857 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
858 {
859 return Fail("%s: projection bias should not be provided without projection weights", __func__);
860 }
861
862 if (desc.m_PeepholeEnabled &&
863 (params.m_CellToForgetWeights == nullptr ||
864 params.m_CellToOutputWeights == nullptr ||
865 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
866 {
867 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
868 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
869 }
870
871 // Check if the layer is supported
872 // Inputs
873 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
874 const armnn::TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
875 const armnn::TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
876
877 // Outputs
878 const armnn::TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
879 const armnn::TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
880 const armnn::TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
881 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
882
883 // Basic parameters
Ferran Balaguer177fa0b2019-07-02 17:34:46 +0100884 armnn::LstmInputParamsInfo paramsInfo;
885 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
886 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
887 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
888 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
889 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
890 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
891 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
892 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
893 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100894
895 if(!desc.m_CifgEnabled)
896 {
Ferran Balaguer177fa0b2019-07-02 17:34:46 +0100897 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
898 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100899 if (params.m_CellToInputWeights != nullptr)
900 {
Ferran Balaguer177fa0b2019-07-02 17:34:46 +0100901 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100902 }
Ferran Balaguer177fa0b2019-07-02 17:34:46 +0100903 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100904 }
905
906 if(desc.m_ProjectionEnabled)
907 {
Ferran Balaguer177fa0b2019-07-02 17:34:46 +0100908 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100909 if (params.m_ProjectionBias != nullptr)
910 {
Ferran Balaguer177fa0b2019-07-02 17:34:46 +0100911 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100912 }
913 }
914
915 if(desc.m_PeepholeEnabled)
916 {
Ferran Balaguer177fa0b2019-07-02 17:34:46 +0100917 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
918 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
arovir01b0717b52018-09-05 17:03:25 +0100919 }
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,
Ferran Balaguer177fa0b2019-07-02 17:34:46 +0100932 paramsInfo))
arovir01b0717b52018-09-05 17:03:25 +0100933 {
934 return false;
935 }
936
937 // Add the layer
938 armnn::IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
939
940 input.Connect(layer->GetInputSlot(0));
941 outputStateIn.Connect(layer->GetInputSlot(1));
942 cellStateIn.Connect(layer->GetInputSlot(2));
943
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100944 return (SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, 0, model, data) &&
945 SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 1, *layer, 1, model, data) &&
946 SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 2, *layer, 2, model, data) &&
947 SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 3, *layer, 3, model, data));
arovir01b0717b52018-09-05 17:03:25 +0100948}
949
950bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
951{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100952 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100953 if (!input.IsValid())
954 {
955 return Fail("%s: Operation has invalid inputs", __func__);
956 }
957
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100958 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +0100959 if (!output)
960 {
961 return Fail("%s: Could not read output 0", __func__);
962 }
963
964 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
965 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
966
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +0100967 if (IsDynamicOutput(outputInfo))
968 {
969 return Fail("%s: Dynamic output not supported", __func__);
970 }
971
Matteo Martincigh58f71092018-09-25 15:58:52 +0100972 armnn::L2NormalizationDescriptor desc;
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +0100973 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matteo Martincigh58f71092018-09-25 15:58:52 +0100974
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100975 if (!IsLayerSupportedForAnyBackend(__func__,
976 armnn::IsL2NormalizationSupported,
977 data.m_Backends,
978 inputInfo,
979 outputInfo,
980 desc))
arovir01b0717b52018-09-05 17:03:25 +0100981 {
982 return false;
983 }
984
Matteo Martincigh58f71092018-09-25 15:58:52 +0100985 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer(desc);
arovir01b0717b52018-09-05 17:03:25 +0100986 assert(layer != nullptr);
Matteo Martincigh5e0ed9f2018-10-01 09:26:32 +0100987 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +0100988
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100989 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100990}
991
992bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data)
993{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100994 return ConvertPooling2d<hal_1_0::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::L2, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100995}
996
997bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data)
998{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100999 return ConvertPooling2d<hal_1_0::HalPolicy>(operation, __func__, armnn::PoolingAlgorithm::Max, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001000}
1001
1002bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
1003{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001004 LayerInputHandle input0 = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
1005 LayerInputHandle input1 = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 1, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001006
1007 if (!input0.IsValid() || !input1.IsValid())
1008 {
1009 return Fail("%s: Operation has invalid inputs", __func__);
1010 }
1011
1012 // The FuseActivation parameter is always the input index 2
1013 // and it should be optional
1014 ActivationFn activationFunction;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001015 if (!GetOptionalInputActivation<hal_1_0::HalPolicy>(operation, 2, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001016 {
1017 return Fail("%s: Operation has invalid inputs", __func__);
1018 }
1019
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001020 const Operand* outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001021
1022 if (outputOperand == nullptr)
1023 {
1024 return false;
1025 }
1026
1027 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
1028
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001029 if (!IsLayerSupportedForAnyBackend(__func__,
1030 armnn::IsMultiplicationSupported,
1031 data.m_Backends,
1032 input0.GetTensorInfo(),
1033 input1.GetTensorInfo(),
1034 outInfo))
arovir01b0717b52018-09-05 17:03:25 +01001035 {
1036 return false;
1037 }
1038
1039 armnn::IConnectableLayer* const startLayer = data.m_Network->AddMultiplicationLayer();
1040 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
1041
1042 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
1043 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
1044
1045 if (endLayer != nullptr)
1046 {
1047 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001048 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001049 }
1050 else
1051 {
1052 return Fail("%s: ProcessActivation failed", __func__);
1053 }
1054}
1055
1056bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, ConversionData& data)
1057{
1058 armnn::ActivationDescriptor desc;
1059 desc.m_Function = armnn::ActivationFunction::ReLu;
1060
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001061 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001062}
1063
1064bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data)
1065{
1066 armnn::ActivationDescriptor desc;
1067 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1068 desc.m_A = 1.0f;
1069 desc.m_B = -1.0f;
1070
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001071 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001072}
1073
1074bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data)
1075{
1076 armnn::ActivationDescriptor desc;
1077 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1078 desc.m_A = 6.0f;
1079
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001080 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001081}
1082
1083bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data)
1084{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001085 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001086 if (!input.IsValid())
1087 {
1088 return Fail("%s: Operation has invalid inputs", __func__);
1089 }
1090
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001091 const Operand* outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001092 if (!outputOperand)
1093 {
1094 return Fail("%s: Operation has no outputs", __func__);
1095 }
1096
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001097 const armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
1098 if (IsDynamicOutput(outputInfo))
1099 {
1100 return Fail("%s: Dynamic output not supported", __func__);
1101 }
arovir01b0717b52018-09-05 17:03:25 +01001102
1103 armnn::SoftmaxDescriptor desc;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001104 if (!GetInputFloat32<hal_1_0::HalPolicy>(operation, 1, desc.m_Beta, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001105 {
1106 return Fail("%s: Operation has invalid inputs", __func__);
1107 }
1108
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001109 if (!IsLayerSupportedForAnyBackend(__func__,
1110 armnn::IsSoftmaxSupported,
1111 data.m_Backends,
1112 input.GetTensorInfo(),
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +01001113 outputInfo,
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001114 desc))
arovir01b0717b52018-09-05 17:03:25 +01001115 {
1116 return false;
1117 }
1118
1119 armnn::IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
1120 assert(layer != nullptr);
1121 input.Connect(layer->GetInputSlot(0));
1122
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001123 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001124}
1125
Keith Davisa6bc52f2019-06-26 09:39:49 +01001126bool HalPolicy::ConvertSpaceToDepth(const Operation& operation, const Model& model, ConversionData& data)
1127{
1128 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
1129
1130 if (!input.IsValid() )
1131 {
1132 return Fail("%s: Operation has invalid inputs", __func__);
1133 }
1134
1135 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1136 unsigned int rank = inputInfo.GetNumDimensions();
1137
1138 if (rank != 4)
1139 {
1140 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1141 }
1142
1143 armnn::SpaceToDepthDescriptor desc;
1144 bool dataLayoutCheck;
1145
1146 GetInputScalar<hal_1_0::HalPolicy>(operation, 1, OperandType::INT32, desc.m_BlockSize, model, data);
1147
1148 if (desc.m_BlockSize <= 1)
1149 {
1150 return Fail("%s: Block size must be at least 1 in all dimensions");
1151 }
1152
1153 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
1154 if (!output)
1155 {
1156 return Fail("%s: Could not read output 0", __func__);
1157 }
1158
1159 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1160 if (!IsLayerSupportedForAnyBackend(__func__,
1161 armnn::IsSpaceToDepthSupported,
1162 data.m_Backends,
1163 inputInfo,
1164 outputInfo,
1165 desc))
1166 {
1167 return false;
1168 }
1169
1170 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
1171 assert(layer != nullptr);
1172 input.Connect(layer->GetInputSlot(0));
1173
1174 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
1175}
1176
arovir01b0717b52018-09-05 17:03:25 +01001177bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data)
1178{
1179 armnn::ActivationDescriptor desc;
1180 desc.m_Function = armnn::ActivationFunction::TanH;
1181 desc.m_A = 1.0f; // android nn does not support tanH parameters
1182 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1183
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001184 return ConvertToActivation<hal_1_0::HalPolicy>(operation, __func__, desc, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001185}
1186
1187bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
1188{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001189 const Operand* inputOperand = GetInputOperand<hal_1_0::HalPolicy>(operation, 0, model);
1190 const Operand* requestedShapeOperand = GetInputOperand<hal_1_0::HalPolicy>(operation, 1, model);
1191 const Operand* outputOperand = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001192
1193 if (inputOperand == nullptr
1194 || requestedShapeOperand == nullptr
1195 || outputOperand == nullptr)
1196 {
1197 return Fail("%s: Operation has invalid inputs", __func__);
1198 }
1199
1200
1201 if (requestedShapeOperand->dimensions.size() != 1)
1202 {
1203 return Fail("%s: Input 1 expected to be one-dimensional (found %i dimensions)",
1204 __func__, requestedShapeOperand->dimensions.size());
1205 }
1206
1207 std::vector<int32_t> targetDimensions;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001208 if (!GetTensorInt32Values<hal_1_0::HalPolicy>(*requestedShapeOperand, targetDimensions, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001209 {
1210 return Fail("%s: Could not read values of input 1", __func__);
1211 }
1212
1213 const Shape inputOperandShape = GetOperandShape(*inputOperand);
1214
1215 Shape requestedShape;
1216 // targetDimensions may contain special values (e.g. -1). reshapePrepare() is an AndroidNN provided utility
1217 // function that resolves these values into a fully specified tensor shape.
1218 if (!reshapePrepare(inputOperandShape, targetDimensions.data(), targetDimensions.size(), &requestedShape))
1219 {
1220 return Fail("%s: Failed to resolve the requested shape", __func__);
1221 }
1222
1223 const Shape outputOperandShape = GetOperandShape(*outputOperand);
1224 if (!SameShape(requestedShape, outputOperandShape))
1225 {
1226 return Fail("%s: Shape of output operand does not match resolved requested shape", __func__);
1227 }
1228
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001229 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001230 if (!input.IsValid())
1231 {
1232 return Fail("%s: Could not read input 0", __func__);
1233 }
1234
arovir01b0717b52018-09-05 17:03:25 +01001235 armnn::ReshapeDescriptor reshapeDescriptor;
1236 reshapeDescriptor.m_TargetShape = armnn::TensorShape(requestedShape.dimensions.size(),
1237 requestedShape.dimensions.data());
1238
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001239 if (!IsLayerSupportedForAnyBackend(__func__,
1240 armnn::IsReshapeSupported,
1241 data.m_Backends,
1242 input.GetTensorInfo(),
1243 reshapeDescriptor))
Matteo Martincigh265d1ad2019-01-08 18:14:53 +00001244 {
1245 return false;
1246 }
1247
arovir01b0717b52018-09-05 17:03:25 +01001248 armnn::IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
1249 assert(layer != nullptr);
1250 input.Connect(layer->GetInputSlot(0));
1251
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001252 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001253}
1254
1255bool HalPolicy::ConvertResizeBilinear(const Operation& operation, const Model& model, ConversionData& data)
1256{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001257 LayerInputHandle input = ConvertToLayerInputHandle<hal_1_0::HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001258 if (!input.IsValid())
1259 {
1260 return Fail("%s: Could not read input 0", __func__);
1261 }
1262
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001263 const Operand* output = GetOutputOperand<hal_1_0::HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001264 if (!output)
1265 {
1266 return Fail("%s: Could not read output 0", __func__);
1267 }
1268
1269 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1270 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1271
Aron Virginas-Tara5daf862019-07-01 19:07:20 +01001272 armnn::ResizeDescriptor desc;
1273 desc.m_Method = armnn::ResizeMethod::Bilinear;
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001274 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001275
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001276 if (!IsLayerSupportedForAnyBackend(__func__,
Aron Virginas-Tara5daf862019-07-01 19:07:20 +01001277 armnn::IsResizeSupported,
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +01001278 data.m_Backends,
1279 inputInfo,
Aron Virginas-Tara5daf862019-07-01 19:07:20 +01001280 outputInfo,
1281 desc))
arovir01b0717b52018-09-05 17:03:25 +01001282 {
1283 return false;
1284 }
1285
Aron Virginas-Tar535607d2019-07-03 15:46:15 +01001286 if (!GetInputScalar<hal_1_0::HalPolicy>(operation, 1, OperandType::INT32, desc.m_TargetWidth, model, data) ||
1287 !GetInputScalar<hal_1_0::HalPolicy>(operation, 2, OperandType::INT32, desc.m_TargetHeight, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001288 {
1289 return Fail("%s: Operation has invalid inputs", __func__);
1290 }
1291
Aron Virginas-Tara5daf862019-07-01 19:07:20 +01001292 armnn::IConnectableLayer* layer = data.m_Network->AddResizeLayer(desc);
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001293
arovir01b0717b52018-09-05 17:03:25 +01001294 assert(layer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +01001295
Mohamed Nour Abouelseoud81afa302018-10-29 14:32:55 +00001296 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1297 input.Connect(layer->GetInputSlot(0));
arovir01b0717b52018-09-05 17:03:25 +01001298
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001299 return SetupAndTrackLayerOutputSlot<hal_1_0::HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001300
1301}
1302
1303} // namespace hal_1_0
Matteo Martincigh58f71092018-09-25 15:58:52 +01001304} // namespace armnn_driver