blob: 32efa540b325045bcef58130d143e5f72221dc1b [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#pragma once
7
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01008#include "Utils.hpp"
9
arovir01b0717b52018-09-05 17:03:25 +010010#include <armnn/ArmNN.hpp>
Ferran Balaguerd30093c2019-07-09 17:04:47 +010011#include <armnn/ILayerSupport.hpp>
12#include <armnn/BackendHelper.hpp>
arovir01b0717b52018-09-05 17:03:25 +010013
Mike Kellyb5fdf382019-06-11 16:35:25 +010014#include "armnn/src/armnnUtils/DataLayoutIndexed.hpp"
arovir01b0717b52018-09-05 17:03:25 +010015#include "armnn/src/armnnUtils/Permute.hpp"
arovir01b0717b52018-09-05 17:03:25 +010016
17#include <ActivationFunctor.h>
18#include <CpuExecutor.h>
19#include <OperationsUtils.h>
20
21#include <boost/assert.hpp>
22#include <boost/core/ignore_unused.hpp>
Aron Virginas-Tar0e7ab542019-04-10 15:02:31 +010023#include <boost/numeric/conversion/cast.hpp>
arovir01b0717b52018-09-05 17:03:25 +010024#include <boost/test/tools/floating_point_comparison.hpp>
25
26#include <log/log.h>
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010027#include <vector>
arovir01b0717b52018-09-05 17:03:25 +010028
29namespace armnn_driver
30{
31
32///
33/// Helper classes
34///
35
36struct ConversionData
37{
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010038 ConversionData(const std::vector<armnn::BackendId>& backends)
39 : m_Backends(backends)
40 , m_Network(nullptr, nullptr)
arovir01b0717b52018-09-05 17:03:25 +010041 {}
42
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010043 const std::vector<armnn::BackendId> m_Backends;
arovir01b0717b52018-09-05 17:03:25 +010044 armnn::INetworkPtr m_Network;
45 std::vector<armnn::IOutputSlot*> m_OutputSlotForOperand;
46 std::vector<android::nn::RunTimePoolInfo> m_MemPools;
47};
48
49class LayerInputHandle
50{
51public:
52 LayerInputHandle();
53 LayerInputHandle(bool valid, armnn::IOutputSlot* outputSlot, armnn::TensorInfo tensorInfo);
54
55 bool IsValid() const;
56
57 void Connect(armnn::IInputSlot& inputSlot);
58
59 const armnn::TensorInfo& GetTensorInfo() const;
60
61private:
62 armnn::IOutputSlot* m_OutputSlot;
63 bool m_Valid;
64 armnn::TensorInfo m_TensorInfo;
65};
66
67class ConstTensorPin
68{
69public:
70 // Creates an invalid tensor pin (can be used to signal errors)
71 // The optional flag can be set to indicate the tensor values were missing, but it was otherwise valid
72 ConstTensorPin(bool optional = false);
73
74 // @param tensorInfo TensorInfo associated with the tensor.
75 // @param valueStart Start address of tensor data. Belongs to one of the memory pools associated with
76 // the model being converted.
77 // @param numBytes Number of bytes for the tensor data.
78 ConstTensorPin(const armnn::TensorInfo& tensorInfo, const void* valueStart, uint32_t numBytes,
79 const armnn::PermutationVector& mappings);
80
81 ConstTensorPin(const ConstTensorPin& other) = delete;
82 ConstTensorPin(ConstTensorPin&& other) = default;
83
84 bool IsValid() const;
85 bool IsOptional() const;
86
87 const armnn::ConstTensor& GetConstTensor() const;
88 const armnn::ConstTensor* GetConstTensorPtr() const;
89
90private:
91 armnn::ConstTensor m_ConstTensor;
92
93 // Owned memory for swizzled tensor data, only required if the tensor needed
94 // swizzling. Otherwise, @ref m_ConstTensor will reference memory from one of
95 // the pools associated with the model being converted.
96 std::vector<uint8_t> m_SwizzledTensorData;
97
98 // optional flag to indicate that an invalid tensor pin is not an error, but the optional values were not given
99 bool m_Optional;
100};
101
102} // namespace armnn_driver
103
104///
105/// Utility functions
106///
107
108namespace
109{
110
111using namespace armnn_driver;
112using namespace android::nn;
113
114// Convenience function to log the reason for failing to convert a model.
115// @return Always returns false (so that it can be used by callers as a quick way to signal an error and return)
116template<class... Args>
117static bool Fail(const char* formatStr, Args&&... args)
118{
119 ALOGD(formatStr, std::forward<Args>(args)...);
120 return false;
121}
122
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100123// Convenience macro to call an Is*Supported function and log caller name together with reason for lack of support.
124// Called as: FORWARD_LAYER_SUPPORT_FUNC(__func__, Is*Supported, backends, a, b, c, d, e)
125#define FORWARD_LAYER_SUPPORT_FUNC(funcName, func, backends, supported, ...) \
126 std::string reasonIfUnsupported; \
127 try { \
128 for (auto&& backendId : backends) \
129 { \
130 auto layerSupportObject = armnn::GetILayerSupportByBackendId(backendId); \
131 if (layerSupportObject) \
132 { \
133 supported = \
134 layerSupportObject->func(__VA_ARGS__, armnn::Optional<std::string&>(reasonIfUnsupported)); \
135 if (supported) \
136 { \
137 break; \
138 } \
139 else \
140 { \
141 if (reasonIfUnsupported.size() > 0) \
142 { \
143 ALOGD("%s: not supported by armnn: %s", funcName, reasonIfUnsupported.c_str()); \
144 } \
145 else \
146 { \
147 ALOGD("%s: not supported by armnn", funcName); \
148 } \
149 } \
150 } \
151 else \
152 { \
153 ALOGD("%s: backend not registered: %s", funcName, backendId.Get().c_str()); \
154 } \
155 } \
156 if (!supported) \
157 { \
158 ALOGD("%s: not supported by any specified backend", funcName); \
159 } \
160 } catch (const armnn::InvalidArgumentException &e) { \
161 throw armnn::InvalidArgumentException(e, "Failed to check layer support", CHECK_LOCATION()); \
arovir01b0717b52018-09-05 17:03:25 +0100162 }
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100163
Mike Kellyb5fdf382019-06-11 16:35:25 +0100164template<typename Operand>
165armnn::TensorShape GetTensorShapeForOperand(const Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100166{
167 return armnn::TensorShape(operand.dimensions.size(), operand.dimensions.data());
168}
169
Matthew Bentham912b3622019-05-03 15:49:14 +0100170inline bool IsOperandTypeSupportedForTensors(V1_0::OperandType type)
arovir01b0717b52018-09-05 17:03:25 +0100171{
Matthew Bentham912b3622019-05-03 15:49:14 +0100172 return type == V1_0::OperandType::TENSOR_FLOAT32 ||
173 type == V1_0::OperandType::TENSOR_QUANT8_ASYMM ||
174 type == V1_0::OperandType::TENSOR_INT32;
arovir01b0717b52018-09-05 17:03:25 +0100175}
176
Mike Kellyb5fdf382019-06-11 16:35:25 +0100177#ifdef ARMNN_ANDROID_NN_V1_2
178
179inline bool IsOperandTypeSupportedForTensors(V1_2::OperandType type)
180{
181 return type == V1_2::OperandType::BOOL ||
182 type == V1_2::OperandType::TENSOR_FLOAT16 ||
183 type == V1_2::OperandType::TENSOR_FLOAT32 ||
184 type == V1_2::OperandType::TENSOR_QUANT8_ASYMM ||
185 type == V1_2::OperandType::TENSOR_QUANT16_SYMM ||
186 type == V1_2::OperandType::TENSOR_INT32;
187}
188
189#endif
190
191inline bool IsBool(V1_0::Operand)
192{
193 return false;
194}
195
Sadik Armagan61113162019-07-25 09:09:40 +0100196inline bool Is12Operand(V1_0::Operand)
197{
198 return false;
199}
200
Mike Kellyb5fdf382019-06-11 16:35:25 +0100201#ifdef ARMNN_ANDROID_NN_V1_2
202
203inline bool IsBool(V1_2::Operand operand)
204{
205 return operand.type == V1_2::OperandType::BOOL;
206}
207
Sadik Armagan61113162019-07-25 09:09:40 +0100208/// Checks if a operand is 1_2 Operand
209inline bool Is12Operand(V1_2::Operand)
210{
211 return true;
212}
213
Mike Kellyb5fdf382019-06-11 16:35:25 +0100214#endif
215
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100216template<typename LayerHandleType>
217armnn::IConnectableLayer& AddReshapeLayer(armnn::INetwork& network, LayerHandleType& inputLayer,
218 armnn::TensorInfo reshapeInfo)
219{
220 armnn::ReshapeDescriptor reshapeDescriptor;
221 reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
222
223 armnn::IConnectableLayer* reshapeLayer = network.AddReshapeLayer(reshapeDescriptor);
224 BOOST_ASSERT(reshapeLayer != nullptr);
225
226 // Attach the input layer to the reshape layer
227 inputLayer.Connect(reshapeLayer->GetInputSlot(0));
228 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapeInfo);
229
230 return *reshapeLayer;
231}
232
233void BroadcastTensor(LayerInputHandle& input0, LayerInputHandle& input1,
234 armnn::IConnectableLayer* startLayer, armnn::INetwork& network)
arovir01b0717b52018-09-05 17:03:25 +0100235{
236 BOOST_ASSERT(startLayer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +0100237
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100238 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
239 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
240
241 unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
242 unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
243
244 if (inputDimensions0 == inputDimensions1)
arovir01b0717b52018-09-05 17:03:25 +0100245 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100246 // The inputs have the same number of dimensions, simply connect them to the given layer as they are
247 input0.Connect(startLayer->GetInputSlot(0));
248 input1.Connect(startLayer->GetInputSlot(1));
249
250 return;
251 }
252
253 // Since the number of dimensions do not match then we need to add degenerate dimensions
254 // to the "smaller" tensor using a reshape, while keeping the order of the inputs.
255
256 unsigned int maxInputDimensions = std::max(inputDimensions0, inputDimensions1);
257 unsigned int sizeDifference = std::abs(boost::numeric_cast<int>(inputDimensions0) -
258 boost::numeric_cast<int>(inputDimensions1));
259
260 bool input0IsSmaller = inputDimensions0 < inputDimensions1;
261 LayerInputHandle& smallInputHandle = input0IsSmaller ? input0 : input1;
262 const armnn::TensorInfo& smallInfo = smallInputHandle.GetTensorInfo();
263
264 const armnn::TensorShape& smallShape = smallInfo.GetShape();
265 std::vector<unsigned int> reshapedDimensions(maxInputDimensions, 1);
266 for (unsigned int i = sizeDifference; i < maxInputDimensions; i++)
267 {
268 reshapedDimensions[i] = smallShape[i - sizeDifference];
269 }
270
271 armnn::TensorInfo reshapedInfo = smallInfo;
272 reshapedInfo.SetShape(armnn::TensorShape{ boost::numeric_cast<unsigned int>(reshapedDimensions.size()),
273 reshapedDimensions.data() });
274 armnn::IConnectableLayer& reshapeLayer = AddReshapeLayer(network, smallInputHandle, reshapedInfo);
275
276 if (input0IsSmaller)
277 {
278 // Input0 is the "smaller" tensor, connect the reshape layer as follows:
279 //
280 // Input0 Input1
arovir01b0717b52018-09-05 17:03:25 +0100281 // | |
282 // Reshape |
283 // \ /
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100284 // StartLayer
arovir01b0717b52018-09-05 17:03:25 +0100285
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100286 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
287 input1.Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100288 }
289 else
290 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100291 // Input1 is the "smaller" tensor, connect the reshape layer as follows:
292 //
293 // Input0 Input1
294 // | |
295 // | Reshape
296 // \ /
297 // StartLayer
298
arovir01b0717b52018-09-05 17:03:25 +0100299 input0.Connect(startLayer->GetInputSlot(0));
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100300 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100301 }
302}
303
304void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t& outPadHead, uint32_t& outPadTail,
305 android::nn::PaddingScheme scheme)
306{
307 int32_t padHead;
308 int32_t padTail;
309 calculateExplicitPadding(input, stride, kernel, scheme, &padHead, &padTail);
310 outPadHead = boost::numeric_cast<uint32_t>(padHead);
311 outPadTail = boost::numeric_cast<uint32_t>(padTail);
312}
313
Mike Kelly86b36d42019-07-12 16:39:33 +0100314#ifdef ARMNN_ANDROID_NN_V1_2
315
316void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t dilation, uint32_t& outPadHead,
317 uint32_t& outPadTail, android::nn::PaddingScheme scheme)
318{
319 int32_t padHead;
320 int32_t padTail;
321 calculateExplicitPadding(input, stride, dilation, kernel, scheme, &padHead, &padTail);
322 outPadHead = boost::numeric_cast<uint32_t>(padHead);
323 outPadTail = boost::numeric_cast<uint32_t>(padTail);
324}
325
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +0100326void CalcPaddingTransposeConv(uint32_t output, uint32_t kernel, uint32_t stride, int32_t& outPadHead,
327 int32_t& outPadTail, android::nn::PaddingScheme scheme)
328{
329 calculateExplicitPaddingTransposeConv(output, stride, kernel, scheme, &outPadHead, &outPadTail);
330}
331
Mike Kelly86b36d42019-07-12 16:39:33 +0100332#endif
333
Matthew Bentham912b3622019-05-03 15:49:14 +0100334Shape GetOperandShape(const V1_0::Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100335{
336 Shape shape;
Matthew Bentham912b3622019-05-03 15:49:14 +0100337 shape.type = OperandType(operand.type);
arovir01b0717b52018-09-05 17:03:25 +0100338 shape.dimensions = operand.dimensions;
339 shape.scale = operand.scale;
340 shape.offset = operand.zeroPoint;
341 return shape;
342}
343
344// ArmNN requires the bias scale to be equal to the product of the weight and input scales, which is also
345// what AndroidNN requires. However for some of the AndroidNN tests the values don't exactly match so
Aron Virginas-Tara0baa172019-08-01 11:24:08 +0100346// we accept some tolerance. We don't want ArmNN itself to accept these inconsistencies as it is up to the
347// user (us, in this case) to ensure they match.
arovir01b0717b52018-09-05 17:03:25 +0100348void SanitizeBiasQuantizationScale(armnn::TensorInfo& biasInfo,
349 const armnn::TensorInfo& weightInfo, const armnn::TensorInfo& inputInfo)
350{
351 const float expectedBiasScale = weightInfo.GetQuantizationScale() * inputInfo.GetQuantizationScale();
352 if (biasInfo.GetQuantizationScale() != expectedBiasScale)
353 {
354 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
355 if (comparer(biasInfo.GetQuantizationScale(), expectedBiasScale))
356 {
357 ALOGW("Bias quantization scale has been modified to match input*weights");
358 biasInfo.SetQuantizationScale(expectedBiasScale);
359 }
360 }
361}
362
363// 4D Tensor Permutations
364const armnn::PermutationVector IdentityPermutation4D({ 0U, 1U, 2U, 3U });
365const armnn::PermutationVector NHWCToArmNN({ 0U, 2U, 3U, 1U });
366const armnn::PermutationVector ArmNNToNHWC({ 0U, 3U, 1U, 2U });
367const armnn::PermutationVector SwapDim1And2({ 0U, 2U, 1U, 3U });
368
369// 3D Permutation Vectors
370const armnn::PermutationVector IdentityPermutation3D({ 0U, 1U, 2U });
371const armnn::PermutationVector RotateTensorLeft({ 2U, 0U, 1U });
372const armnn::PermutationVector RotateTensorRight({ 1U, 2U, 0U });
373
374template<typename OSlot>
375armnn::IConnectableLayer& AddPermuteLayer(armnn::INetwork& network, OSlot& input,
376 const armnn::PermutationVector& mappings)
377{
378 // Add swizzle layer
379 armnn::IConnectableLayer* const layer = network.AddPermuteLayer(mappings);
380
381 BOOST_ASSERT(layer != nullptr);
382
383 // Connect input to swizzle layer
384 input.Connect(layer->GetInputSlot(0));
385
386 // Setup swizzled output
387 const armnn::TensorInfo outInfo = armnnUtils::Permuted(input.GetTensorInfo(), mappings);
388 layer->GetOutputSlot(0).SetTensorInfo(outInfo);
389
390 return *layer;
391}
392
393void SwizzleIn(armnn::INetwork& network, LayerInputHandle& input, armnn::IConnectableLayer& layer, unsigned int index)
394{
395 // Add swizzle layer
396 armnn::IConnectableLayer& swizzleLayer = AddPermuteLayer(network, input, NHWCToArmNN);
397 // Connect swizzled input to layer
398 swizzleLayer.GetOutputSlot(0).Connect(layer.GetInputSlot(index));
399}
400
401armnn::IConnectableLayer& DeswizzleOut(armnn::INetwork& network, armnn::IConnectableLayer& layer, unsigned int index)
402{
403 // Add deswizzle layer
404 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(network, layer.GetOutputSlot(index), ArmNNToNHWC);
405 return deswizzleLayer;
406}
407
408// only suitable for input/output slot index 0, for other slots, use SwizzleIn and DeswizzleOut directly
409armnn::IConnectableLayer& SwizzleInDeswizzleOut(armnn::INetwork& network,
410 LayerInputHandle& input,
411 armnn::IConnectableLayer& firstLayer,
412 armnn::IConnectableLayer& lastLayer)
413{
414 SwizzleIn(network, input, firstLayer, 0);
415 return DeswizzleOut(network, lastLayer, 0);
416}
417
418// only suitable for input/output slot index 0, for other slots, use SwizzleIn and DeswizzleOut directly
419armnn::IConnectableLayer& SwizzleInDeswizzleOut(armnn::INetwork& network, LayerInputHandle& input,
420 armnn::IConnectableLayer& layer)
421{
422 return SwizzleInDeswizzleOut(network, input, layer, layer);
423}
424
425bool ValidateConcatOutputShape(const std::vector<armnn::TensorShape> & inputShapes,
426 const armnn::TensorShape & outputShape,
427 uint32_t concatDim)
428{
429 // Validate the output shape is correct given the input shapes (which have just been validated)
430 unsigned int numDimensions = inputShapes[0].GetNumDimensions();
431 if (outputShape.GetNumDimensions() != numDimensions)
432 {
433 return Fail("%s: Output shape has wrong number of dimensions", __func__);
434 }
435
436 unsigned int outputSizeAlongConcatenatedDimension = 0;
437 for (unsigned int i = 0; i < inputShapes.size(); i++)
438 {
439 outputSizeAlongConcatenatedDimension += inputShapes[i][concatDim];
440 }
441
442 for (unsigned int i = 0; i < numDimensions; ++i)
443 {
444 if (i == concatDim)
445 {
446 if (outputShape[i] != outputSizeAlongConcatenatedDimension)
447 {
448 return Fail(
449 "%s: Invalid output shape for dimension %d (%d != %d)",
450 __func__,
451 i,
452 outputShape[i],
453 outputSizeAlongConcatenatedDimension);
454 }
455 }
456 else
457 {
458 if (outputShape[i] != inputShapes[0][i])
459 {
460 return Fail("%s: Invalid output shape", __func__);
461 }
462 }
463 }
464
465 return true;
466}
467
468bool RequiresReshape(armnn::TensorShape & inputShape)
469{
470 return inputShape.GetNumDimensions() < 3;
471}
472
arovir01b0717b52018-09-05 17:03:25 +0100473void SwizzleInputs(armnn::INetwork& network,
474 std::vector<LayerInputHandle>& inputs,
475 std::vector<armnn::TensorShape>& inputShapes,
476 const armnn::PermutationVector& mapping)
477{
478 if (!mapping.IsEqual(IdentityPermutation4D))
479 {
480 size_t nInputs = inputs.size();
481 for (size_t i=0; i<nInputs; ++i)
482 {
483 // add swizzle layer
484 armnn::IConnectableLayer& swizzleLayer = AddPermuteLayer(network, inputs[i], mapping);
485 auto& outputSlot = swizzleLayer.GetOutputSlot(0);
486 auto& outputInfo = outputSlot.GetTensorInfo();
487 // replace inputs with the swizzled ones
488 inputs[i] = LayerInputHandle(true, &outputSlot, outputInfo);
489 inputShapes[i] = inputs[i].GetTensorInfo().GetShape();
490 }
491 }
492}
493
narpra01f176d5a2018-11-18 20:17:48 +0000494bool CreateConcatPermutationParameters(const unsigned int numberOfDimensions,
495 int32_t & concatDimension,
496 std::pair<armnn::PermutationVector, armnn::PermutationVector> & permutationPair)
arovir01b0717b52018-09-05 17:03:25 +0100497{
narpra01f176d5a2018-11-18 20:17:48 +0000498 bool needPermute = false;
arovir01b0717b52018-09-05 17:03:25 +0100499 BOOST_ASSERT(numberOfDimensions >= 3);
500
501 // ArmNN uses Compute Library subtensors to perform concatenation
narpra01f176d5a2018-11-18 20:17:48 +0000502 // This only works when concatenating along dimension 0, 1 or 3 for a 4-D tensor,
503 // or along dimension 0 or 2 for a 3-D tensor.
504 if (numberOfDimensions == 4 && concatDimension == 2)
arovir01b0717b52018-09-05 17:03:25 +0100505 {
narpra01f176d5a2018-11-18 20:17:48 +0000506 concatDimension = 1;
507 permutationPair = std::make_pair(SwapDim1And2, SwapDim1And2);
508 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100509 }
narpra01f176d5a2018-11-18 20:17:48 +0000510 else if (numberOfDimensions == 3 && concatDimension == 1)
arovir01b0717b52018-09-05 17:03:25 +0100511 {
narpra01f176d5a2018-11-18 20:17:48 +0000512 concatDimension = 0;
513 permutationPair = std::make_pair(RotateTensorLeft, RotateTensorRight);
514 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100515 }
narpra01f176d5a2018-11-18 20:17:48 +0000516 return needPermute;
arovir01b0717b52018-09-05 17:03:25 +0100517}
518
519} // anonymous namespace
520
521namespace armnn_driver
522{
523
524//// Creates an ArmNN activation layer and connects it to the given layer, if the
525//// passed in AndroidNN activation function requires so.
526//// @return The end layer of the sequence of layers built for the given AndroidNN
527//// activation function or nullptr if an error occurred (e.g. unsupported activation).
528//// Note that the end layer matches the input layer if no activation is required
529//// (the sequence of layers has length 1).
530armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
531 ActivationFn activation,
532 armnn::IConnectableLayer* prevLayer,
533 ConversionData& data);
534
535} // namespace armnn_driver
536
537///
538/// Utility templates
539///
540
541namespace armnn_driver
542{
543
544using namespace android::nn;
545
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100546template<typename HalPolicy,
547 typename HalOperand = typename HalPolicy::Operand,
548 typename HalOperation = typename HalPolicy::Operation,
549 typename HalModel = typename HalPolicy::Model>
550const HalOperand* GetInputOperand(const HalOperation& operation,
551 uint32_t inputIndex,
552 const HalModel& model,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100553 bool failOnIndexOutOfBounds = true)
arovir01b0717b52018-09-05 17:03:25 +0100554{
555 if (inputIndex >= operation.inputs.size())
556 {
saoste01b8471482018-10-10 09:44:51 +0100557 if (failOnIndexOutOfBounds)
558 {
559 Fail("%s: invalid input index: %i out of %i", __func__, inputIndex, operation.inputs.size());
560 }
arovir01b0717b52018-09-05 17:03:25 +0100561 return nullptr;
562 }
563
564 BOOST_ASSERT(operation.inputs[inputIndex] < model.operands.size()); // Model should have been validated beforehand
565 return &model.operands[operation.inputs[inputIndex]];
566}
567
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100568template<typename HalPolicy,
569 typename HalOperand = typename HalPolicy::Operand,
570 typename HalOperation = typename HalPolicy::Operation,
571 typename HalModel = typename HalPolicy::Model>
572const HalOperand* GetOutputOperand(const HalOperation& operation,
573 uint32_t outputIndex,
574 const HalModel& model)
arovir01b0717b52018-09-05 17:03:25 +0100575{
576 if (outputIndex >= operation.outputs.size())
577 {
578 Fail("%s: invalid output index: %i out of %i", __func__, outputIndex, operation.outputs.size());
579 return nullptr;
580 }
581
582 // Model should have been validated beforehand
583 BOOST_ASSERT(operation.outputs[outputIndex] < model.operands.size());
584
585 return &model.operands[operation.outputs[outputIndex]];
586}
587
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100588template<typename HalPolicy,
589 typename HalOperand = typename HalPolicy::Operand,
590 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100591const void* GetOperandValueReadOnlyAddress(const HalOperand& operand,
Matthew Bentham912b3622019-05-03 15:49:14 +0100592 const HalModel& model,
593 const ConversionData& data,
Kevin Mayf29a2c52019-03-14 11:56:32 +0000594 bool optional = false)
arovir01b0717b52018-09-05 17:03:25 +0100595{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100596 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
arovir01b0717b52018-09-05 17:03:25 +0100597
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100598 const void* valueStart = nullptr;
arovir01b0717b52018-09-05 17:03:25 +0100599 switch (operand.lifetime)
600 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100601 case HalOperandLifeTime::CONSTANT_COPY:
arovir01b0717b52018-09-05 17:03:25 +0100602 {
603 // Constant found in model.operandValues
604 valueStart = &model.operandValues[operand.location.offset];
605 break;
606 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100607 case HalOperandLifeTime::CONSTANT_REFERENCE:
arovir01b0717b52018-09-05 17:03:25 +0100608 {
609 // Constant specified via a Memory object
610 valueStart = GetMemoryFromPool(operand.location, data.m_MemPools);
611 break;
612 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100613 case HalOperandLifeTime::NO_VALUE:
Kevin Mayf29a2c52019-03-14 11:56:32 +0000614 {
615 // An optional input tensor with no values is not an error so should not register as a fail
616 if (optional)
617 {
618 valueStart = nullptr;
619 break;
620 }
Matthew Bentham912b3622019-05-03 15:49:14 +0100621 [[fallthrough]];
Kevin Mayf29a2c52019-03-14 11:56:32 +0000622 }
arovir01b0717b52018-09-05 17:03:25 +0100623 default:
624 {
625 // Unsupported/invalid (e.g. can't get value of an input to the model)
626 Fail("%s: unsupported/invalid operand lifetime: %s",
627 __func__, toString(operand.lifetime).c_str());
628 valueStart = nullptr;
629 }
630 }
631
632 return valueStart;
633}
634
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100635template<typename HalPolicy,
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100636 typename HalOperation = typename HalPolicy::Operation,
637 typename HalModel = typename HalPolicy::Model,
638 typename HalOperandType = typename HalPolicy::OperandType>
639bool GetOperandType(const HalOperation& operation,
640 uint32_t inputIndex,
641 const HalModel& model,
642 HalOperandType& type)
643{
644 using HalOperand = typename HalPolicy::Operand;
645
646 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
647 if (!operand)
648 {
649 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
650 }
651
652 type = operand->type;
653 return true;
654}
655
656template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100657 typename HalOperand = typename HalPolicy::Operand,
658 typename HalModel = typename HalPolicy::Model>
659ConstTensorPin ConvertOperandToConstTensorPin(const HalOperand& operand,
660 const HalModel& model,
661 const ConversionData& data,
662 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
663 const armnn::TensorShape* overrideTensorShape = nullptr,
664 bool optional = false)
665{
666 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
667
668 if (!IsOperandTypeSupportedForTensors(operand.type))
669 {
670 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand.type).c_str());
671 return ConstTensorPin();
672 }
673
674 if (!optional &&
675 operand.lifetime != HalOperandLifeTime::CONSTANT_COPY &&
676 operand.lifetime != HalOperandLifeTime::CONSTANT_REFERENCE &&
677 operand.lifetime != HalOperandLifeTime::NO_VALUE)
678 {
679 Fail("%s: invalid operand lifetime: %s", __func__, toString(operand.lifetime).c_str());
680 return ConstTensorPin();
681 }
682
683 const void* const valueStart = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data, optional);
684 if (!valueStart)
685 {
686 if (optional)
687 {
688 // optional tensor with no values is not really an error; return it as invalid, but marked as optional
689 return ConstTensorPin(true);
690 }
691 // mandatory tensor with no values
692 Fail("%s: failed to get operand address", __func__);
693 return ConstTensorPin();
694 }
695
696 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(operand);
697 if (overrideTensorShape != nullptr)
698 {
699 tensorInfo.SetShape(*overrideTensorShape);
700 }
701 return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings);
702}
703
704template<typename HalPolicy,
705 typename HalOperation = typename HalPolicy::Operation,
706 typename HalModel = typename HalPolicy::Model>
707ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operation,
708 uint32_t inputIndex,
709 const HalModel& model,
710 const ConversionData& data,
711 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
712 const armnn::TensorShape* overrideTensorShape = nullptr,
713 bool optional = false)
714{
715 using HalOperand = typename HalPolicy::Operand;
716
717 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
718 if (!operand)
719 {
720 Fail("%s: failed to get input operand: index=%u", __func__, inputIndex);
721 return ConstTensorPin();
722 }
723 return ConvertOperandToConstTensorPin<HalPolicy>(*operand,
724 model,
725 data,
726 dimensionMappings,
727 overrideTensorShape,
728 optional);
729}
730
731template<typename HalPolicy,
732 typename OutputType,
733 typename HalOperandType = typename HalPolicy::OperandType,
734 typename HalOperation = typename HalPolicy::Operation,
735 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100736bool GetInputScalar(const HalOperation& operation,
737 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100738 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100739 OutputType& outValue,
740 const HalModel& model,
741 const ConversionData& data)
742{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100743 using HalOperand = typename HalPolicy::Operand;
744
745 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +0100746 if (!operand)
747 {
748 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
749 }
750
751 if (operand->type != type)
752 {
753 return Fail("%s: unexpected operand type: %s (should be %s)",
754 __func__, toString(operand->type).c_str(), toString(type).c_str());
755 }
756
757 if (operand->location.length != sizeof(OutputType))
758 {
759 return Fail("%s: incorrect operand location length: %i (should be %i)",
760 __func__, operand->location.length, sizeof(OutputType));
761 }
762
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100763 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100764 if (!valueAddress)
765 {
766 return Fail("%s: failed to get address for operand", __func__);
767 }
768
769 outValue = *(static_cast<const OutputType*>(valueAddress));
770 return true;
771}
772
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100773template<typename HalPolicy,
774 typename HalOperation = typename HalPolicy::Operation,
775 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100776bool GetInputInt32(const HalOperation& operation,
777 uint32_t inputIndex,
778 int32_t& outValue,
779 const HalModel& model,
780 const ConversionData& data)
781{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100782 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::INT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100783}
784
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100785template<typename HalPolicy,
786 typename HalOperation = typename HalPolicy::Operation,
787 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100788bool GetInputFloat32(const HalOperation& operation,
789 uint32_t inputIndex,
790 float& outValue,
791 const HalModel& model,
792 const ConversionData& data)
793{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100794 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::FLOAT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100795}
796
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100797template<typename HalPolicy,
798 typename HalOperation = typename HalPolicy::Operation,
799 typename HalOperandType = typename HalPolicy::OperandType,
800 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100801bool GetInputActivationFunctionImpl(const HalOperation& operation,
802 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100803 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100804 ActivationFn& outActivationFunction,
805 const HalModel& model,
806 const ConversionData& data)
807{
Mike Kellyb5fdf382019-06-11 16:35:25 +0100808 if (type != HalOperandType::INT32 && type != HalOperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100809 {
810 return Fail("%s: unexpected operand type: %s (should be %s or %s)",
811 __func__,
812 toString(type).c_str(),
813 toString(OperandType::INT32).c_str(),
814 toString(OperandType::TENSOR_INT32).c_str());
815 }
816
817 int32_t activationFunctionAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100818 if (!GetInputScalar<HalPolicy>(operation, inputIndex, type, activationFunctionAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100819 {
820 return Fail("%s: failed to get activation input value", __func__);
821 }
822 outActivationFunction = static_cast<ActivationFn>(activationFunctionAsInt);
823 return true;
824}
825
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100826template<typename HalPolicy,
827 typename HalOperation = typename HalPolicy::Operation,
828 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100829bool GetInputActivationFunction(const HalOperation& operation,
830 uint32_t inputIndex,
831 ActivationFn& outActivationFunction,
832 const HalModel& model,
833 const ConversionData& data)
834{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100835 return GetInputActivationFunctionImpl<HalPolicy>(operation,
836 inputIndex,
837 HalPolicy::OperandType::INT32,
838 outActivationFunction,
839 model,
840 data);
arovir01b0717b52018-09-05 17:03:25 +0100841}
842
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100843template<typename HalPolicy,
844 typename HalOperation = typename HalPolicy::Operation,
845 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100846bool GetInputActivationFunctionFromTensor(const HalOperation& operation,
847 uint32_t inputIndex,
848 ActivationFn& outActivationFunction,
849 const HalModel& model,
850 const ConversionData& data)
851{
852 // This only accepts a 1-D tensor of size 1
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100853 return GetInputActivationFunctionImpl<HalPolicy>(operation,
854 inputIndex,
855 HalPolicy::OperandType::INT32,
856 outActivationFunction,
857 model,
858 data);
arovir01b0717b52018-09-05 17:03:25 +0100859}
860
861
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100862template<typename HalPolicy,
863 typename HalOperation = typename HalPolicy::Operation,
864 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100865bool GetOptionalInputActivation(const HalOperation& operation,
866 uint32_t inputIndex,
867 ActivationFn& activationFunction,
868 const HalModel& model,
869 const ConversionData& data)
870{
871 if (operation.inputs.size() <= inputIndex)
872 {
873 activationFunction = ActivationFn::kActivationNone;
874 }
875 else
876 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100877 if (!GetInputActivationFunction<HalPolicy>(operation, inputIndex, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100878 {
879 return Fail("%s: Operation has invalid inputs", __func__);
880 }
881 }
882 return true;
883}
884
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100885template<typename HalPolicy,
886 typename ConvolutionDescriptor,
887 typename HalOperation = typename HalPolicy::Operation,
888 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100889bool GetOptionalConvolutionDilationParams(const HalOperation& operation,
890 uint32_t dilationXIndex,
891 ConvolutionDescriptor& descriptor,
892 const HalModel& model,
893 const ConversionData& data)
894{
895 bool success = true;
896 if (operation.inputs.size() >= dilationXIndex + 2)
897 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100898 success &= GetInputScalar<HalPolicy>(operation,
899 dilationXIndex,
900 HalPolicy::OperandType::INT32,
901 descriptor.m_DilationX,
902 model,
903 data);
904 success &= GetInputScalar<HalPolicy>(operation,
905 dilationXIndex + 1,
906 HalPolicy::OperandType::INT32,
907 descriptor.m_DilationY,
908 model,
909 data);
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100910 }
911
912 return success;
913}
914
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100915template<typename HalPolicy,
916 typename HalOperand = typename HalPolicy::Operand,
917 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100918bool GetTensorInt32Values(const HalOperand& operand,
arovir01b0717b52018-09-05 17:03:25 +0100919 std::vector<int32_t>& outValues,
920 const HalModel& model,
921 const ConversionData& data)
922{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100923 if (operand.type != HalPolicy::OperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100924 {
925 return Fail("%s: invalid operand type: %s", __func__, toString(operand.type).c_str());
926 }
927
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100928 const void* startAddress = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100929 if (!startAddress)
930 {
931 return Fail("%s: failed to get operand address", __func__, operand.type);
932 }
933
934 // Check number of bytes is sensible
935 const uint32_t numBytes = operand.location.length;
936 if (numBytes % sizeof(int32_t) != 0)
937 {
938 return Fail("%s: invalid number of bytes: %i, expected to be a multiple of %i",
939 __func__, numBytes, sizeof(int32_t));
940 }
941
942 outValues.resize(numBytes / sizeof(int32_t));
943 memcpy(outValues.data(), startAddress, numBytes);
944 return true;
945}
946
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100947template<typename HalPolicy,
948 typename HalOperation = typename HalPolicy::Operation,
949 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100950bool GetInputPaddingScheme(const HalOperation& operation,
951 uint32_t inputIndex,
952 PaddingScheme& outPaddingScheme,
953 const HalModel& model,
954 const ConversionData& data)
955{
956 int32_t paddingSchemeAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100957 if (!GetInputInt32<HalPolicy>(operation, inputIndex, paddingSchemeAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100958 {
959 return Fail("%s: failed to get padding scheme input value", __func__);
960 }
961
962 outPaddingScheme = static_cast<android::nn::PaddingScheme>(paddingSchemeAsInt);
963 return true;
964}
965
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100966template<typename HalPolicy,
967 typename HalOperation = typename HalPolicy::Operation,
968 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100969LayerInputHandle ConvertToLayerInputHandle(const HalOperation& operation,
970 uint32_t inputIndex,
971 const HalModel& model,
972 ConversionData& data)
973{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100974 using HalOperand = typename HalPolicy::Operand;
Sadik Armagan44bcc022019-06-18 17:21:36 +0100975 using HalOperandType = typename HalPolicy::OperandType;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100976 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
977
978 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +0100979 if (!operand)
980 {
981 Fail("%s: failed to get input operand %i", __func__, inputIndex);
982 return LayerInputHandle();
983 }
984
985 if (!IsOperandTypeSupportedForTensors(operand->type))
986 {
987 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand->type).c_str());
988 return LayerInputHandle();
989 }
990
Sadik Armagan44bcc022019-06-18 17:21:36 +0100991 try
arovir01b0717b52018-09-05 17:03:25 +0100992 {
Sadik Armagan44bcc022019-06-18 17:21:36 +0100993 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100994 if (IsDynamicTensor(operandTensorInfo))
995 {
996 Fail("%s: dynamic input tensors are not supported", __func__);
997 return LayerInputHandle();
998 }
arovir01b0717b52018-09-05 17:03:25 +0100999
Sadik Armagan44bcc022019-06-18 17:21:36 +01001000 switch (operand->lifetime)
arovir01b0717b52018-09-05 17:03:25 +01001001 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001002 case HalOperandLifeTime::MODEL_INPUT:
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001003 {
1004 // NOTE: We must check whether we can support the input tensor on at least one
1005 // of the provided backends; otherwise we cannot convert the operation
1006 bool isInputSupported = false;
1007 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1008 IsInputSupported,
1009 data.m_Backends,
1010 isInputSupported,
1011 operandTensorInfo);
1012
1013 if (!isInputSupported)
1014 {
1015 Fail("%s: unsupported input tensor", __func__);
1016 return LayerInputHandle();
1017 }
1018
1019 BOOST_FALLTHROUGH; // intentional fallthrough
1020 }
1021 case HalOperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001022 case HalOperandLifeTime::MODEL_OUTPUT:
arovir01b0717b52018-09-05 17:03:25 +01001023 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001024 // The tensor is either an operand internal to the model, or a model input.
1025 // It can be associated with an ArmNN output slot for an existing layer.
1026
1027 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
1028 const uint32_t operandIndex = operation.inputs[inputIndex];
1029 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
Sadik Armagan44bcc022019-06-18 17:21:36 +01001030 }
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001031 case HalOperandLifeTime::CONSTANT_COPY: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001032 case HalOperandLifeTime::CONSTANT_REFERENCE:
1033 {
1034 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
1035 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin<HalPolicy>(*operand, model, data);
1036 if (tensorPin.IsValid())
arovir01b0717b52018-09-05 17:03:25 +01001037 {
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001038 bool isSupported = false;
1039 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1040 IsConstantSupported,
1041 data.m_Backends,
1042 isSupported,
1043 tensorPin.GetConstTensor().GetInfo());
Mike Kelly28e3d9f2019-08-07 14:55:04 +01001044 if (!isSupported)
Sadik Armagan44bcc022019-06-18 17:21:36 +01001045 {
1046 return LayerInputHandle();
1047 }
1048
1049 armnn::IConnectableLayer* constantLayer =
1050 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
1051 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
1052 outputSlot.SetTensorInfo(tensorPin.GetConstTensor().GetInfo());
1053
1054 return LayerInputHandle(true, &outputSlot, operandTensorInfo);
1055 }
1056 else
1057 {
1058 Fail("%s: invalid operand tensor", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001059 return LayerInputHandle();
1060 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001061 break;
arovir01b0717b52018-09-05 17:03:25 +01001062 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001063 default:
arovir01b0717b52018-09-05 17:03:25 +01001064 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001065 // Unsupported lifetime for an input tensor
1066 Fail("%s: unsupported lifetime for input tensor: %s",
1067 __func__, toString(operand->lifetime).c_str());
arovir01b0717b52018-09-05 17:03:25 +01001068 return LayerInputHandle();
1069 }
arovir01b0717b52018-09-05 17:03:25 +01001070 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001071 }
1072 catch (UnsupportedOperand<HalOperandType>& e)
1073 {
1074 Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str());
1075 return LayerInputHandle();
arovir01b0717b52018-09-05 17:03:25 +01001076 }
1077}
1078
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001079template<typename HalPolicy,
1080 typename HalOperation = typename HalPolicy::Operation,
1081 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001082bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1083 uint32_t operationOutputIndex,
1084 armnn::IConnectableLayer& layer,
1085 uint32_t layerOutputIndex,
1086 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001087 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001088{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001089 using HalOperand = typename HalPolicy::Operand;
1090
1091 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001092 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
1093 {
1094 return false;
1095 }
1096
1097 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
1098
1099 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
1100 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
1101
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001102 outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand));
Mike Kellyb5fdf382019-06-11 16:35:25 +01001103
1104 return true;
1105}
1106
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001107template<typename HalPolicy,
1108 typename HalOperation = typename HalPolicy::Operation,
1109 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001110armnn::DataLayout OptionalDataLayout(const HalOperation& operation,
1111 uint32_t inputIndex,
1112 const HalModel& model,
1113 ConversionData& data)
1114{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001115 using HalOperand = typename HalPolicy::Operand;
1116
1117 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001118 if (!operand)
1119 {
1120 return armnn::DataLayout::NHWC;
1121 }
1122
1123 if (!IsBool(*operand))
1124 {
1125 return armnn::DataLayout::NHWC;
1126 }
1127
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001128 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001129 if (!valueAddress)
1130 {
1131 return armnn::DataLayout::NHWC;
1132 }
1133
1134 if (*(static_cast<const bool*>(valueAddress)))
1135 {
1136 return armnn::DataLayout::NCHW;
1137 }
1138 else
1139 {
1140 return armnn::DataLayout::NHWC;
1141 }
1142}
1143
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001144template<typename HalPolicy,
1145 typename HalOperation = typename HalPolicy::Operation,
1146 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001147bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1148 uint32_t outputIndex,
1149 armnn::IConnectableLayer& layer,
1150 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001151 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001152{
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001153 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1154 outputIndex,
1155 layer,
1156 outputIndex,
1157 model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001158 data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001159}
1160
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001161template<typename HalPolicy,
1162 typename HalOperation = typename HalPolicy::Operation,
1163 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001164bool ConvertToActivation(const HalOperation& operation,
1165 const char* operationName,
1166 const armnn::ActivationDescriptor& activationDesc,
1167 const HalModel& model,
1168 ConversionData& data)
1169{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001170 using HalOperand = typename HalPolicy::Operand;
1171
1172 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001173 if (!input.IsValid())
1174 {
1175 return Fail("%s: Input 0 is invalid", operationName);
1176 }
1177
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001178 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001179 if (!outputOperand)
1180 {
1181 return false;
1182 }
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001183
1184 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan2050c232019-07-23 16:59:58 +01001185 if (IsDynamicTensor(outInfo))
1186 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001187 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan2050c232019-07-23 16:59:58 +01001188 }
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001189
1190 bool isSupported = false;
1191 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1192 IsActivationSupported,
1193 data.m_Backends,
1194 isSupported,
1195 input.GetTensorInfo(),
1196 outInfo,
1197 activationDesc);
1198 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001199 {
1200 return false;
1201 }
1202
1203 armnn::IConnectableLayer* layer = data.m_Network->AddActivationLayer(activationDesc);
1204 BOOST_ASSERT(layer != nullptr);
1205 input.Connect(layer->GetInputSlot(0));
1206
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001207 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001208}
1209
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001210template<typename HalPolicy,
Sadik Armagan61113162019-07-25 09:09:40 +01001211 typename HalOperation = typename HalPolicy::Operation,
1212 typename HalModel = typename HalPolicy::Model>
1213bool ConvertReLu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1214{
1215 armnn::ActivationDescriptor desc;
1216 desc.m_Function = armnn::ActivationFunction::ReLu;
1217
1218 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1219}
1220
1221template<typename HalPolicy,
1222 typename HalOperation = typename HalPolicy::Operation,
1223 typename HalModel = typename HalPolicy::Model>
1224bool ConvertReLu1(const HalOperation& operation, const HalModel& model, ConversionData& data)
1225{
1226 armnn::ActivationDescriptor desc;
1227 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1228 desc.m_A = 1.0f;
1229 desc.m_B = -1.0f;
1230
1231 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1232}
1233
1234template<typename HalPolicy,
1235 typename HalOperation = typename HalPolicy::Operation,
1236 typename HalModel = typename HalPolicy::Model>
1237bool ConvertReLu6(const HalOperation& operation, const HalModel& model, ConversionData& data)
1238{
1239 armnn::ActivationDescriptor desc;
1240 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1241 desc.m_A = 6.0f;
1242
1243 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1244}
1245
1246template<typename HalPolicy,
1247 typename HalOperation = typename HalPolicy::Operation,
1248 typename HalModel = typename HalPolicy::Model>
1249bool ConvertTanH(const HalOperation& operation, const HalModel& model, ConversionData& data)
1250{
1251 armnn::ActivationDescriptor desc;
1252 desc.m_Function = armnn::ActivationFunction::TanH;
1253 desc.m_A = 1.0f; // android nn does not support tanH parameters
1254 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1255
1256 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1257}
1258
1259template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001260 typename HalOperation = typename HalPolicy::Operation,
1261 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001262bool ConvertPaddings(const HalOperation& operation,
1263 const HalModel& model,
1264 ConversionData& data,
1265 unsigned int rank,
1266 armnn::PadDescriptor& padDescriptor)
1267{
1268 using HalOperand = typename HalPolicy::Operand;
1269
1270 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
1271 if (!paddingsOperand)
1272 {
1273 return Fail("%s: Could not read paddings operand", __func__);
1274 }
1275
1276 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
1277 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2)
1278 {
1279 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
1280 }
1281
1282 std::vector<int32_t> paddings;
1283 GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data);
1284
1285 // add padding for each dimension of input tensor.
1286 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
1287 {
1288 int paddingBeforeInput = paddings[i];
1289 int paddingAfterInput = paddings[i + 1];
1290
1291 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
1292 {
1293 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
1294 }
1295
1296 padDescriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
1297 }
1298
1299 return true;
1300}
1301
1302template<typename HalPolicy,
1303 typename HalOperation = typename HalPolicy::Operation,
1304 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001305bool ConvertPooling2d(const HalOperation& operation,
1306 const char* operationName,
1307 armnn::PoolingAlgorithm poolType,
1308 const HalModel& model,
1309 ConversionData& data)
1310{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001311 using HalOperand = typename HalPolicy::Operand;
1312 using HalOperandType = typename HalPolicy::OperandType;
1313
1314 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001315 if (!input.IsValid())
1316 {
1317 return Fail("%s: Could not read input 0", operationName);
1318 }
1319
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001320 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001321 if (!output)
1322 {
1323 return Fail("%s: Could not read output 0", __func__);
1324 }
1325
1326 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1327 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1328
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001329 if (IsDynamicTensor(outputInfo))
1330 {
1331 return Fail("%s: Dynamic output tensors are not supported", __func__);
1332 }
1333
arovir01b0717b52018-09-05 17:03:25 +01001334 armnn::Pooling2dDescriptor desc;
1335 desc.m_PoolType = poolType;
1336 desc.m_OutputShapeRounding = armnn::OutputShapeRounding::Floor;
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001337 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001338
1339 ActivationFn activation;
1340
Sadik Armagan15d63e22019-07-26 16:59:35 +01001341 auto inputSize = operation.inputs.size();
1342
1343 if (inputSize >= 10)
1344 {
1345 // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type)
1346 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1347 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1348 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1349 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1350 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1351 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1352 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1353 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1354 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
1355 {
1356 return Fail("%s: Operation has invalid inputs", operationName);
1357 }
1358
1359 if (Is12Operand(*output))
1360 {
1361 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
1362 }
1363 }
1364 else
arovir01b0717b52018-09-05 17:03:25 +01001365 {
1366 // one input, 6 parameters (padding, stridex, stridey, width, height, activation type)
1367 android::nn::PaddingScheme scheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001368 if (!GetInputPaddingScheme<HalPolicy>(operation, 1, scheme, model, data) ||
1369 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1370 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1371 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1372 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1373 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001374 {
1375 return Fail("%s: Operation has invalid inputs", operationName);
1376 }
1377
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001378 const unsigned int inputWidth = inputInfo.GetShape()[2];
1379 const unsigned int inputHeight = inputInfo.GetShape()[1];
arovir01b0717b52018-09-05 17:03:25 +01001380
1381 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, scheme);
1382 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, scheme);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001383
1384 if (Is12Operand(*output))
arovir01b0717b52018-09-05 17:03:25 +01001385 {
Sadik Armagan15d63e22019-07-26 16:59:35 +01001386 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001387 }
1388 }
1389
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001390 bool isSupported = false;
1391 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1392 IsPooling2dSupported,
1393 data.m_Backends,
1394 isSupported,
1395 inputInfo,
1396 outputInfo,
1397 desc);
1398 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001399 {
Éanna Ó Catháin3d1059c2018-10-11 15:53:04 +01001400 return false;
arovir01b0717b52018-09-05 17:03:25 +01001401 }
arovir01b0717b52018-09-05 17:03:25 +01001402
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001403 armnn::IConnectableLayer* pooling2dLayer = data.m_Network->AddPooling2dLayer(desc);
1404 if (!pooling2dLayer)
arovir01b0717b52018-09-05 17:03:25 +01001405 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001406 return Fail("%s: AddPooling2dLayer failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001407 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001408
1409 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, pooling2dLayer, data);
1410 if (!endLayer)
arovir01b0717b52018-09-05 17:03:25 +01001411 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001412 return Fail("%s: ProcessActivation failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001413 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001414
1415 input.Connect(pooling2dLayer->GetInputSlot(0));
1416
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001417 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001418}
1419
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001420template<typename HalPolicy,
Mike Kellyb8805202019-07-31 17:25:43 +01001421 typename Operation = typename HalPolicy::Operation,
1422 typename Model = typename HalPolicy::Model>
1423bool ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
1424{
1425 using HalOperand = typename HalPolicy::Operand;
1426 using HalOperandType = typename HalPolicy::OperandType;
1427
1428 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
1429 if (operation.inputs.size() <= 1)
1430 {
1431 return Fail("%s: Operation has insufficient arguments", __func__);
1432 }
1433
1434 // Get inputs and outputs
1435 const std::size_t numInputTensors = operation.inputs.size() - 1;
1436
1437 int32_t concatDim;
1438 if (!GetInputScalar<HalPolicy>(operation, numInputTensors, HalOperandType::INT32, concatDim, model, data))
1439 {
1440 return Fail("%s: Operation has invalid inputs", __func__);
1441 }
1442
1443 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1444 if (!outputOperand)
1445 {
1446 return Fail("%s: Operation has no outputs", __func__);
1447 }
1448
1449
1450 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
1451 armnn::TensorShape outputShape = outputInfo.GetShape();
1452
1453 //
1454 // handle negative concat dims along the lines of tensorflow as described here:
1455 // https://www.tensorflow.org/api_docs/python/tf/concat
1456 // "negative axis refers to axis + rank(values)-th dimension"
1457 //
1458 if (concatDim < 0)
1459 {
1460 concatDim += outputShape.GetNumDimensions();
1461 }
1462
1463 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
1464 {
1465 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
1466 }
1467
1468 std::vector<LayerInputHandle> inputHandles;
1469 std::vector<armnn::TensorShape> inputShapes;
1470
1471 inputHandles.reserve(numInputTensors);
1472 inputShapes.reserve(numInputTensors);
1473
1474 bool inputsHaveBeenReshaped = false;
1475 unsigned int tensorDimensionsAdded = 0;
1476
1477 for (uint32_t i = 0; i < numInputTensors; ++i)
1478 {
1479 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, i, model);
1480 if (!operand)
1481 {
1482 return Fail("%s: Operation has invalid inputs", __func__);
1483 }
1484
1485 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
1486 LayerInputHandle operandInputHandle =
1487 ConvertToLayerInputHandle<HalPolicy>(operation, i, model, data);
1488
1489 if (operandShape.GetNumDimensions() == 0)
1490 {
1491 return Fail("%s: Operands with rank 0 are not supported", __func__);
1492 }
1493
1494 if (RequiresReshape(operandShape))
1495 {
1496 inputsHaveBeenReshaped = true;
1497
1498 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
1499
1500 // Expand the tensor to three dimensions
1501 if (operandShape.GetNumDimensions() == 2)
1502 {
1503 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
1504 tensorDimensionsAdded = 1;
1505 }
1506 else
1507 {
1508 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
1509 tensorDimensionsAdded = 2;
1510 }
1511
1512 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
1513 *data.m_Network,
1514 operandInputHandle,
1515 reshapeInfo
1516 );
1517
1518 // Point to the reshape operation rather then the input operation
1519 operandShape = reshapeInfo.GetShape();
1520 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
1521 }
1522
1523 inputShapes.emplace_back(operandShape);
1524 inputHandles.emplace_back(operandInputHandle);
1525
1526 if (!inputHandles.back().IsValid())
1527 {
1528 return Fail("%s: Operation has invalid inputs", __func__);
1529 }
1530 }
1531
1532 BOOST_ASSERT(inputShapes.size() == inputHandles.size());
1533
1534 if (inputsHaveBeenReshaped)
1535 {
1536 // Adjust the concatenation dimension by the amount of dimensions added (if any)
1537 concatDim += tensorDimensionsAdded;
1538
1539 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
1540 if (tensorDimensionsAdded == 1)
1541 {
1542 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
1543 }
1544 else if (tensorDimensionsAdded == 2)
1545 {
1546 outputShape = armnn::TensorShape({1, 1, outputShape[0]});
1547 }
1548 }
1549
1550 // Check if permutations is required and get the pair of permutations required for the concatenation.
1551 // Permutation is required when the concat dimension is 2 for a 4D tensor or 1 for a 3D tensor.
1552 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
1553 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
1554
1555 bool needPermute =
1556 CreateConcatPermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
1557
1558 if (needPermute)
1559 {
1560 outputShape = armnnUtils::Permuted(outputShape, permutationPair.first);
1561 }
1562
1563 outputInfo.SetShape(outputShape);
1564
1565 // this is no-op for identity swizzles, otherwise it replaces both
1566 // the handles and shapes with the swizzled layer output handles and shapes
1567 SwizzleInputs(*data.m_Network, inputHandles, inputShapes, permutationPair.first);
1568
1569 // Create an armnn concat layer descriptor - this will also perform validation on the input shapes
1570 armnn::OriginsDescriptor concatDescriptor;
1571
1572 try
1573 {
1574 // The concat descriptor is always created across the only supported concat dimension
1575 // which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
1576 concatDescriptor =
1577 armnn::CreateDescriptorForConcatenation(inputShapes.begin(), inputShapes.end(), concatDim);
1578 }
1579 catch (const armnn::Exception& error)
1580 {
1581 return Fail("%s: Error preparing concat descriptor. %s", __func__, error.what());
1582 }
1583
1584 // Validate the output shape is correct given the input shapes based on the
1585 // only valid concat dimension which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
1586 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
1587 {
1588 return Fail("%s: Error validating the output shape for concat", __func__);
1589 }
1590
1591 std::vector<const armnn::TensorInfo*> inputTensorInfos;
1592 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
1593 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
1594
1595 bool isSupported = false;
1596 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1597 IsConcatSupported,
1598 data.m_Backends,
1599 isSupported,
1600 inputTensorInfos,
1601 outputInfo,
1602 concatDescriptor);
1603 if (!isSupported)
1604 {
1605 return false;
1606 }
1607
1608 armnn::IConnectableLayer* layer = data.m_Network->AddConcatLayer(concatDescriptor);
1609 assert(layer != nullptr);
1610 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1611
1612 // Connect inputs to the layer
1613 const int numInputSlots = layer->GetNumInputSlots();
1614 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
1615 for (int i = 0; i < numInputSlots; ++i)
1616 {
1617 // connect the input directly to the merge (concat) layer
1618 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
1619 }
1620
1621 if (needPermute)
1622 {
1623 // Add permutation layer and connect the output to it, the permutation becomes the output layer
1624 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(*data.m_Network,
1625 layer->GetOutputSlot(0),
1626 permutationPair.second);
1627 layer = &deswizzleLayer;
1628 }
1629
1630 if (inputsHaveBeenReshaped)
1631 {
1632 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
1633
1634 // Undo the reshape knowing the amount of dimensions added
1635 if (tensorDimensionsAdded == 1)
1636 {
1637 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
1638 afterConcatInfo.GetShape()[2] }));
1639 }
1640 else if (tensorDimensionsAdded == 2)
1641 {
1642 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2] }));
1643 }
1644
1645 layer = &AddReshapeLayer(
1646 *data.m_Network,
1647 layer->GetOutputSlot(0),
1648 afterConcatInfo
1649 );
1650 }
1651
1652 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1653}
1654
1655template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001656 typename HalOperation = typename HalPolicy::Operation,
1657 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001658bool ConvertConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1659{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001660 using HalOperand = typename HalPolicy::Operand;
1661 using HalOperandType = typename HalPolicy::OperandType;
1662
1663 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001664 if (!input.IsValid())
1665 {
1666 return Fail("%s: Operation has invalid inputs", __func__);
1667 }
1668
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001669 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001670 if (!output)
1671 {
1672 return Fail("%s: Could not read output 0", __func__);
1673 }
1674
1675 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001676 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001677
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001678 if (IsDynamicTensor(outputInfo))
1679 {
1680 return Fail("%s: Dynamic output tensors are not supported", __func__);
1681 }
1682
Mike Kellyb5fdf382019-06-11 16:35:25 +01001683 // ArmNN does not currently support non-fixed weights or bias
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001684 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1685 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001686
1687 if (!weightsPin.IsValid() || !biasPin.IsValid())
1688 {
1689 return Fail("%s: Operation has invalid inputs", __func__);
1690 }
1691
1692 armnn::ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001693 armnn::ConstTensor bias = biasPin.GetConstTensor();
Mike Kellyb5fdf382019-06-11 16:35:25 +01001694 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1695
1696 armnn::Convolution2dDescriptor desc;
1697 desc.m_DataLayout = armnn::DataLayout::NHWC;
1698 ActivationFn activation;
1699
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001700 if (operation.inputs.size() == 10)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001701 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001702 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1703 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1704 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1705 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1706 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1707 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001708 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001709 {
1710 return Fail("%s: Operation has invalid inputs", __func__);
1711 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01001712 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001713 else if (operation.inputs.size() == 7)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001714 {
1715 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001716 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1717 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1718 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001719 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001720 {
1721 return Fail("%s: Operation has invalid inputs", __func__);
1722 }
1723
1724 const uint32_t kernelX = weights.GetShape()[2];
1725 const uint32_t kernelY = weights.GetShape()[1];
1726 const uint32_t inputX = inputInfo.GetShape()[2];
1727 const uint32_t inputY = inputInfo.GetShape()[1];
1728
1729 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1730 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001731 }
1732 else
1733 {
1734 return Fail("%s: Unsupported number of operation inputs", __func__);
1735 }
1736
1737 desc.m_BiasEnabled = true;
1738 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1739
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001740 bool isSupported = false;
1741 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1742 IsConvolution2dSupported,
1743 data.m_Backends,
1744 isSupported,
1745 inputInfo,
1746 outputInfo,
1747 desc,
1748 weights.GetInfo(),
1749 biases);
1750 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001751 {
1752 return false;
1753 }
1754
1755 armnn::IConnectableLayer* startLayer =
1756 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1757
1758 if (!startLayer)
1759 {
1760 return Fail("%s: AddConvolution2dLayer failed", __func__);
1761 }
1762
1763 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1764
1765 if (!endLayer)
1766 {
1767 return Fail("%s: ProcessActivation failed", __func__);
1768 }
1769
1770 input.Connect(startLayer->GetInputSlot(0));
1771
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001772 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001773}
1774
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001775template<typename HalPolicy,
1776 typename HalOperation = typename HalPolicy::Operation,
1777 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001778bool ConvertDepthwiseConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1779{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001780 using HalOperand = typename HalPolicy::Operand;
1781 using HalOperandType = typename HalPolicy::OperandType;
1782
1783 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001784
1785 if (!input.IsValid())
1786 {
1787 return Fail("%s: Operation has invalid inputs", __func__);
1788 }
1789
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001790 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001791
1792 if (!output)
1793 {
1794 return Fail("%s: Could not read output 0", __func__);
1795 }
1796
1797 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001798 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001799
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001800 if (IsDynamicTensor(outputInfo))
1801 {
1802 return Fail("%s: Dynamic output tensors are not supported", __func__);
1803 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01001804
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001805 // ArmNN does not currently support non-fixed weights or bias
Mike Kellyb5fdf382019-06-11 16:35:25 +01001806 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001807 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001808
1809 if (weightsOperand == nullptr)
1810 {
1811 return Fail("%s: Operand is invalid", __func__);
1812 }
1813 armnn::DepthwiseConvolution2dDescriptor desc;
1814 desc.m_DataLayout = armnn::DataLayout::NHWC;
1815
Mike Kellyb5fdf382019-06-11 16:35:25 +01001816 // Reinterpret weight data as [ H, W, I, M ]
1817 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
1818 weightsOperand->dimensions[2],
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001819 inputInfo.GetShape()[3],
1820 weightsOperand->dimensions[3] / inputInfo.GetShape()[3] });
Mike Kellyb5fdf382019-06-11 16:35:25 +01001821
1822 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
1823 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
1824
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001825 const ConstTensorPin weightsPin =
1826 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
1827 1,
1828 model,
1829 data,
1830 HWIMToMIHW,
1831 &weightsShape);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001832
1833 // Bias is a 1D tensor
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001834 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001835
1836 if (!weightsPin.IsValid() || !biasPin.IsValid())
1837 {
1838 return Fail("%s: Operation has invalid inputs", __func__);
1839 }
1840
1841 armnn::ConstTensor weights = weightsPin.GetConstTensor();
1842 armnn::ConstTensor bias = biasPin.GetConstTensor();
1843 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1844
1845 ActivationFn activation;
1846
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001847 if (operation.inputs.size() == 11)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001848 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001849 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1850 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1851 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1852 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1853 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1854 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001855 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001856 {
1857 return Fail("%s: Operation has invalid inputs", __func__);
1858 }
1859 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001860 else if (operation.inputs.size() == 8)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001861 {
1862 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001863 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1864 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1865 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001866 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001867 {
1868 return Fail("%s: Operation has invalid inputs", __func__);
1869 }
1870
1871 const uint32_t kernelX = weights.GetShape()[3];
1872 const uint32_t kernelY = weights.GetShape()[2];
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001873 const uint32_t inputX = inputInfo.GetShape()[2];
1874 const uint32_t inputY = inputInfo.GetShape()[1];
Mike Kellyb5fdf382019-06-11 16:35:25 +01001875
1876 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1877 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1878 }
1879 else
1880 {
1881 return Fail("%s: Unsupported number of operation inputs", __func__);
1882 }
1883
1884 desc.m_BiasEnabled = true;
1885 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1886
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001887 bool isSupported = false;
1888 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1889 IsDepthwiseConvolutionSupported,
1890 data.m_Backends,
1891 isSupported,
1892 inputInfo,
1893 outputInfo,
1894 desc,
1895 weights.GetInfo(),
1896 biases);
1897 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001898 {
1899 return false;
1900 }
1901
1902 armnn::IConnectableLayer* startLayer =
1903 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1904 if (!startLayer)
1905 {
1906 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
1907 }
1908
1909 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1910 if (!endLayer)
1911 {
1912 return Fail("%s: ProcessActivation failed", __func__);
1913 }
1914
1915 input.Connect(startLayer->GetInputSlot(0));
1916
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001917 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001918}
1919
Mike Kelly3c673942019-07-25 09:26:06 +01001920template<typename HalPolicy,
1921 typename HalOperation = typename HalPolicy::Operation,
1922 typename HalOperand = typename HalPolicy::Operand,
1923 typename HalModel = typename HalPolicy::Model>
1924bool ConvertPad(HalOperation& operation, const HalModel& model, ConversionData& data)
1925{
Mike Kelly3c673942019-07-25 09:26:06 +01001926 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1927 if (!input.IsValid())
1928 {
1929 return Fail("%s: Operation has invalid inputs", __func__);
1930 }
1931
1932 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1933 unsigned int rank = inputInfo.GetNumDimensions();
1934
1935 armnn::PadDescriptor descriptor;
1936 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1937 {
1938 return Fail("%s: Could not convert paddings", __func__);
1939 }
1940
1941 // Before Android Q, the pad value for ANEURALNETWORKS_TENSOR_QUANT8_ASYMM was undefined. Since Android Q the pad
1942 // value must be "logical zero" we set it to be equal to the QuantizationOffset so effectively it ends up as
1943 // (QuantizationOffset - QuantizationOffset) * scale = 0.
1944 if (inputInfo.GetDataType() == armnn::DataType::QuantisedAsymm8)
1945 {
1946 descriptor.m_PadValue = inputInfo.GetQuantizationOffset();
1947 }
1948
1949 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1950 if (!output)
1951 {
1952 return Fail("%s: Could not read output", __func__);
1953 }
1954
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001955 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kelly3c673942019-07-25 09:26:06 +01001956 if (IsDynamicTensor(outputInfo))
1957 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001958 return Fail("%s: Dynamic output tensors are not supported", __func__);
Mike Kelly3c673942019-07-25 09:26:06 +01001959 }
1960
1961 bool isSupported = false;
1962 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1963 IsPadSupported,
1964 data.m_Backends,
1965 isSupported,
1966 inputInfo,
1967 outputInfo,
1968 descriptor);
1969 if (!isSupported)
1970 {
1971 return false;
1972 }
1973
1974 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
1975 assert(layer != nullptr);
1976 input.Connect(layer->GetInputSlot(0));
1977 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1978
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001979 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +01001980}
1981
Mike Kelly0a879362019-07-29 16:56:31 +01001982template<typename HalPolicy,
1983 typename Operation = typename HalPolicy::Operation,
1984 typename Operand = typename HalPolicy::Operand,
1985 typename Model = typename HalPolicy::Model>
1986bool ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
1987{
1988 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1989 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1990
1991 if (!input0.IsValid() || !input1.IsValid())
1992 {
1993 return Fail("%s: Operation has invalid inputs", __func__);
1994 }
1995
1996 // The FuseActivation parameter is always the input index 2
1997 // and it should be optional
1998 ActivationFn activationFunction;
1999 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2000 {
2001 return Fail("%s: Operation has invalid inputs", __func__);
2002 }
2003
2004 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2005 if (!output)
2006 {
2007 return Fail("%s: Could not read output 0", __func__);
2008 }
2009
2010 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2011 if (IsDynamicTensor(outputInfo))
2012 {
2013 return Fail("%s: Dynamic output tensors are not supported", __func__);
2014 }
2015
2016 bool isSupported = false;
2017 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2018 IsSubtractionSupported,
2019 data.m_Backends,
2020 isSupported,
2021 input0.GetTensorInfo(),
2022 input1.GetTensorInfo(),
2023 outputInfo);
2024 if (!isSupported)
2025 {
2026 return false;
2027 }
2028
2029 armnn::IConnectableLayer* const startLayer = data.m_Network->AddSubtractionLayer();
2030 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2031
2032 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
2033 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
2034
2035 if (endLayer)
2036 {
2037 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
2038 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2039 }
2040
2041 return Fail("%s: ProcessActivation failed", __func__);
2042}
2043
Finn Williams23b87b32019-07-30 11:44:05 +01002044template<typename HalPolicy,
2045 typename HalOperation = typename HalPolicy::Operation,
Finn Williams0e4e4392019-07-31 10:56:27 +01002046 typename HalOperand = typename HalPolicy::Operand,
Finn Williams23b87b32019-07-30 11:44:05 +01002047 typename HalModel = typename HalPolicy::Model>
2048bool ConvertBatchToSpaceNd(const HalOperation& operation,
2049 const HalModel& model,
2050 ConversionData& data)
2051{
Finn Williams23b87b32019-07-30 11:44:05 +01002052
2053 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2054 if (!input.IsValid())
2055 {
2056 return Fail("%s: Operation has invalid inputs", __func__);
2057 }
2058
2059 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2060 if (!output)
2061 {
2062 return Fail("%s: Could not read output 0", __func__);
2063 }
2064
2065 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2066 if (IsDynamicTensor(outputInfo))
2067 {
2068 return Fail("%s: Dynamic output tensors are not supported", __func__);
2069 }
2070
2071 const HalOperand* blockOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2072 if (!blockOperand)
2073 {
2074 return Fail("%s: Could not read input 1", __func__);
2075 }
2076
2077 // Convert the block operand to int32
2078 std::vector<int32_t> block;
2079 if (!GetTensorInt32Values<HalPolicy>(*blockOperand, block, model, data))
2080 {
2081 return Fail("%s: Input 1 has invalid values", __func__);
2082 }
2083
2084 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2085
2086 unsigned int rank = inputInfo.GetNumDimensions();
2087 if (rank != 4)
2088 {
2089 Fail("%s: Only inputs with rank equal to 4 are supported", __func__);
2090 }
2091
2092 if (std::any_of(block.cbegin(), block.cend(), [](int32_t i){ return i < 1; }))
2093 {
2094 return Fail("%s: Block sizes for each spatial dimension of the input tensor must be"
2095 " greater than or equal to 1", __func__);
2096 }
2097
2098 armnn::BatchToSpaceNdDescriptor batchToSpaceNdDesc;
2099 batchToSpaceNdDesc.m_BlockShape.assign(block.cbegin(), block.cend());
2100 batchToSpaceNdDesc.m_DataLayout = armnn::DataLayout::NHWC;
2101
2102 if (Is12Operand(*output))
2103 {
Finn Williams0e4e4392019-07-31 10:56:27 +01002104 batchToSpaceNdDesc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +01002105 }
2106 // Setting crops to 0,0 0,0 as it is not supported in Android NN API
2107 batchToSpaceNdDesc.m_Crops = {{0, 0}, {0, 0}};
2108
2109 bool isSupported = false;
2110 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2111 IsBatchToSpaceNdSupported,
2112 data.m_Backends,
2113 isSupported,
2114 inputInfo,
2115 outputInfo,
2116 batchToSpaceNdDesc);
2117 if (!isSupported)
2118 {
2119 return false;
2120 }
2121
2122 armnn::IConnectableLayer* const layer = data.m_Network->AddBatchToSpaceNdLayer(batchToSpaceNdDesc);
2123 assert(layer != nullptr);
2124 input.Connect(layer->GetInputSlot(0));
2125
2126 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2127}
Mike Kelly0a879362019-07-29 16:56:31 +01002128
Finn Williamsd74c5052019-07-30 17:06:00 +01002129template<typename HalPolicy,
2130 typename HalOperation = typename HalPolicy::Operation,
2131 typename HalOperand = typename HalPolicy::Operand,
2132 typename HalModel = typename HalPolicy::Model>
2133bool ConvertSpaceToBatchNd(const HalOperation& operation, const HalModel& model, ConversionData& data)
2134{
2135 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2136 if (!input.IsValid())
2137 {
2138 return Fail("%s: Operation has invalid inputs", __func__);
2139 }
2140
2141 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2142 unsigned int rank = inputInfo.GetNumDimensions();
2143 unsigned int spatialDim = rank - 2;
2144
2145 if (rank != 4)
2146 {
2147 Fail("%s: Only inputs with rank 4 are supported", __func__);
2148 }
2149
2150 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2151 if (!output)
2152 {
2153 return Fail("%s: Could not read output 0", __func__);
2154 }
2155
2156 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2157 if (IsDynamicTensor(outputInfo))
2158 {
2159 return Fail("%s: Dynamic output tensors are not supported", __func__);
2160 }
2161
2162 const HalOperand* blockShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2163 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 2, model);
2164
2165 armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand(*blockShapeOperand);
2166 if (blockShapeOperandShape.GetNumDimensions() != 1 || blockShapeOperandShape.GetNumElements() != spatialDim)
2167 {
2168 return Fail("%s: Operation has invalid block shape operand: expected shape [%d]", __func__, spatialDim);
2169 }
2170
2171 std::vector<int32_t> blockShape;
2172 GetTensorInt32Values<HalPolicy>(*blockShapeOperand, blockShape, model, data);
2173 if (std::any_of(blockShape.cbegin(), blockShape.cend(), [](int32_t i){ return i < 1; }))
2174 {
2175 return Fail("%s: Block shape must be at least 1 in all dimensions.", __func__);
2176 }
2177
2178 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
2179 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != 2 * spatialDim)
2180 {
2181 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, spatialDim);
2182 }
2183
2184 std::vector<std::pair<unsigned int, unsigned int>> paddingList;
2185 std::vector<int32_t> paddings;
2186 GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data);
2187 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
2188 {
2189 int paddingBeforeInput = paddings[i];
2190 int paddingAfterInput = paddings[i + 1];
2191 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
2192 {
2193 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
2194 }
2195
2196 paddingList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
2197 }
2198
2199 armnn::SpaceToBatchNdDescriptor descriptor;
2200 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
2201 descriptor.m_BlockShape.assign(blockShape.cbegin(), blockShape.cend());
2202 descriptor.m_PadList.assign(paddingList.cbegin(), paddingList.cend());
2203
2204 if (Is12Operand(*output))
2205 {
2206 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
2207 }
2208
2209 bool isSupported = false;
2210 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2211 IsSpaceToBatchNdSupported,
2212 data.m_Backends,
2213 isSupported,
2214 inputInfo,
2215 outputInfo,
2216 descriptor);
2217 if (!isSupported)
2218 {
2219 return false;
2220 }
2221
2222 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToBatchNdLayer(descriptor);
2223 assert(layer != nullptr);
2224 input.Connect(layer->GetInputSlot(0));
2225
2226 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2227}
2228
saoste01b8471482018-10-10 09:44:51 +01002229} // namespace armnn_driver