blob: 3b01b40f617cda4461b3952aa60e282c9e3f6aff [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>
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +010013#include <armnn/utility/Assert.hpp>
Jan Eilers0b7a4192020-03-09 18:20:42 +000014#include <armnn/utility/IgnoreUnused.hpp>
arovir01b0717b52018-09-05 17:03:25 +010015
Matteo Martincigh00d6ed12019-11-28 17:13:24 +000016#include <armnnUtils/DataLayoutIndexed.hpp>
Mike Kelly4a956582020-02-28 10:32:09 +000017#include <armnnUtils/Transpose.hpp>
arovir01b0717b52018-09-05 17:03:25 +010018
Mike Kelly46272802019-08-14 17:00:48 +010019#include "1.0/FullyConnected.hpp"
20
arovir01b0717b52018-09-05 17:03:25 +010021#include <ActivationFunctor.h>
22#include <CpuExecutor.h>
23#include <OperationsUtils.h>
24
Aron Virginas-Tar0e7ab542019-04-10 15:02:31 +010025#include <boost/numeric/conversion/cast.hpp>
arovir01b0717b52018-09-05 17:03:25 +010026#include <boost/test/tools/floating_point_comparison.hpp>
27
28#include <log/log.h>
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010029#include <vector>
arovir01b0717b52018-09-05 17:03:25 +010030
31namespace armnn_driver
32{
33
34///
35/// Helper classes
36///
37
Kevin Mayec1e5b82020-02-26 17:00:39 +000038#ifdef ARMNN_ANDROID_R
39using OperandType = android::nn::hal::OperandType;
40#endif
41
arovir01b0717b52018-09-05 17:03:25 +010042struct ConversionData
43{
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010044 ConversionData(const std::vector<armnn::BackendId>& backends)
45 : m_Backends(backends)
46 , m_Network(nullptr, nullptr)
arovir01b0717b52018-09-05 17:03:25 +010047 {}
48
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010049 const std::vector<armnn::BackendId> m_Backends;
arovir01b0717b52018-09-05 17:03:25 +010050 armnn::INetworkPtr m_Network;
51 std::vector<armnn::IOutputSlot*> m_OutputSlotForOperand;
52 std::vector<android::nn::RunTimePoolInfo> m_MemPools;
53};
54
55class LayerInputHandle
56{
57public:
58 LayerInputHandle();
59 LayerInputHandle(bool valid, armnn::IOutputSlot* outputSlot, armnn::TensorInfo tensorInfo);
60
61 bool IsValid() const;
62
63 void Connect(armnn::IInputSlot& inputSlot);
64
65 const armnn::TensorInfo& GetTensorInfo() const;
66
67private:
68 armnn::IOutputSlot* m_OutputSlot;
69 bool m_Valid;
70 armnn::TensorInfo m_TensorInfo;
71};
72
73class ConstTensorPin
74{
75public:
76 // Creates an invalid tensor pin (can be used to signal errors)
77 // The optional flag can be set to indicate the tensor values were missing, but it was otherwise valid
78 ConstTensorPin(bool optional = false);
79
80 // @param tensorInfo TensorInfo associated with the tensor.
81 // @param valueStart Start address of tensor data. Belongs to one of the memory pools associated with
82 // the model being converted.
83 // @param numBytes Number of bytes for the tensor data.
84 ConstTensorPin(const armnn::TensorInfo& tensorInfo, const void* valueStart, uint32_t numBytes,
85 const armnn::PermutationVector& mappings);
86
87 ConstTensorPin(const ConstTensorPin& other) = delete;
88 ConstTensorPin(ConstTensorPin&& other) = default;
89
90 bool IsValid() const;
91 bool IsOptional() const;
92
93 const armnn::ConstTensor& GetConstTensor() const;
94 const armnn::ConstTensor* GetConstTensorPtr() const;
95
96private:
97 armnn::ConstTensor m_ConstTensor;
98
99 // Owned memory for swizzled tensor data, only required if the tensor needed
100 // swizzling. Otherwise, @ref m_ConstTensor will reference memory from one of
101 // the pools associated with the model being converted.
102 std::vector<uint8_t> m_SwizzledTensorData;
103
104 // optional flag to indicate that an invalid tensor pin is not an error, but the optional values were not given
105 bool m_Optional;
106};
107
108} // namespace armnn_driver
109
110///
111/// Utility functions
112///
113
114namespace
115{
116
117using namespace armnn_driver;
118using namespace android::nn;
119
120// Convenience function to log the reason for failing to convert a model.
121// @return Always returns false (so that it can be used by callers as a quick way to signal an error and return)
122template<class... Args>
123static bool Fail(const char* formatStr, Args&&... args)
124{
125 ALOGD(formatStr, std::forward<Args>(args)...);
126 return false;
127}
128
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100129// Convenience macro to call an Is*Supported function and log caller name together with reason for lack of support.
130// Called as: FORWARD_LAYER_SUPPORT_FUNC(__func__, Is*Supported, backends, a, b, c, d, e)
131#define FORWARD_LAYER_SUPPORT_FUNC(funcName, func, backends, supported, ...) \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100132try \
133{ \
134 for (auto&& backendId : backends) \
135 { \
136 auto layerSupportObject = armnn::GetILayerSupportByBackendId(backendId); \
137 if (layerSupportObject) \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100138 { \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100139 std::string reasonIfUnsupported; \
140 supported = \
141 layerSupportObject->func(__VA_ARGS__, armnn::Optional<std::string&>(reasonIfUnsupported)); \
142 if (supported) \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100143 { \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100144 break; \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100145 } \
146 else \
147 { \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100148 if (reasonIfUnsupported.size() > 0) \
149 { \
150 ALOGD("%s: not supported by armnn: %s", funcName, reasonIfUnsupported.c_str()); \
151 } \
152 else \
153 { \
154 ALOGD("%s: not supported by armnn", funcName); \
155 } \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100156 } \
157 } \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100158 else \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100159 { \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100160 ALOGD("%s: backend not registered: %s", funcName, backendId.Get().c_str()); \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100161 } \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100162 } \
163 if (!supported) \
164 { \
165 ALOGD("%s: not supported by any specified backend", funcName); \
166 } \
167} \
168catch (const armnn::InvalidArgumentException &e) \
169{ \
170 throw armnn::InvalidArgumentException(e, "Failed to check layer support", CHECK_LOCATION()); \
171}
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100172
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +0000173template<typename HalOperand>
174armnn::TensorShape GetTensorShapeForOperand(const HalOperand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100175{
176 return armnn::TensorShape(operand.dimensions.size(), operand.dimensions.data());
177}
178
Matthew Bentham912b3622019-05-03 15:49:14 +0100179inline bool IsOperandTypeSupportedForTensors(V1_0::OperandType type)
arovir01b0717b52018-09-05 17:03:25 +0100180{
Matthew Bentham912b3622019-05-03 15:49:14 +0100181 return type == V1_0::OperandType::TENSOR_FLOAT32 ||
182 type == V1_0::OperandType::TENSOR_QUANT8_ASYMM ||
183 type == V1_0::OperandType::TENSOR_INT32;
arovir01b0717b52018-09-05 17:03:25 +0100184}
185
Kevin May42477c12020-03-26 13:34:14 +0000186#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100187
Keith Davis71006492020-01-06 17:44:16 +0000188// Support within the 1.2 driver for specific tensor data types
Mike Kellyb5fdf382019-06-11 16:35:25 +0100189inline bool IsOperandTypeSupportedForTensors(V1_2::OperandType type)
190{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000191 return type == V1_2::OperandType::BOOL ||
Sadik Armagan793a70c2020-03-19 13:54:04 +0000192 type == V1_2::OperandType::TENSOR_BOOL8 ||
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000193 type == V1_2::OperandType::TENSOR_FLOAT16 ||
194 type == V1_2::OperandType::TENSOR_FLOAT32 ||
195 type == V1_2::OperandType::TENSOR_QUANT8_ASYMM ||
Keith Davis71006492020-01-06 17:44:16 +0000196 type == V1_2::OperandType::TENSOR_QUANT8_SYMM ||
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000197 type == V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
198 type == V1_2::OperandType::TENSOR_QUANT16_SYMM ||
Mike Kellyb5fdf382019-06-11 16:35:25 +0100199 type == V1_2::OperandType::TENSOR_INT32;
200}
201
202#endif
203
Kevin May42477c12020-03-26 13:34:14 +0000204#ifdef ARMNN_ANDROID_NN_V1_3
205
206// Support within the 1.3 driver for specific tensor data types
207inline bool IsOperandTypeSupportedForTensors(V1_3::OperandType type)
208{
209 return type == V1_3::OperandType::BOOL ||
Sadik Armagan51ba2c62020-03-31 15:36:25 +0100210 type == V1_3::OperandType::TENSOR_BOOL8 ||
Kevin May42477c12020-03-26 13:34:14 +0000211 type == V1_3::OperandType::TENSOR_FLOAT16 ||
212 type == V1_3::OperandType::TENSOR_FLOAT32 ||
213 type == V1_3::OperandType::TENSOR_QUANT8_ASYMM ||
Sadik Armagan51ba2c62020-03-31 15:36:25 +0100214 type == V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED ||
Kevin May42477c12020-03-26 13:34:14 +0000215 type == V1_3::OperandType::TENSOR_QUANT8_SYMM ||
216 type == V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
217 type == V1_3::OperandType::TENSOR_QUANT16_SYMM ||
218 type == V1_3::OperandType::TENSOR_INT32;
219}
220
221#endif
222
Mike Kellyb5fdf382019-06-11 16:35:25 +0100223inline bool IsBool(V1_0::Operand)
224{
225 return false;
226}
227
Kevin May42477c12020-03-26 13:34:14 +0000228inline bool Is12OrLaterOperand(V1_0::Operand)
Sadik Armagan61113162019-07-25 09:09:40 +0100229{
230 return false;
231}
232
Kevin May42477c12020-03-26 13:34:14 +0000233#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3)
Mike Kellyb5fdf382019-06-11 16:35:25 +0100234
235inline bool IsBool(V1_2::Operand operand)
236{
237 return operand.type == V1_2::OperandType::BOOL;
238}
239
Sadik Armagan61113162019-07-25 09:09:40 +0100240/// Checks if a operand is 1_2 Operand
Kevin May42477c12020-03-26 13:34:14 +0000241inline bool Is12OrLaterOperand(V1_2::Operand)
242{
243 return true;
244}
245
246#endif
247
248#ifdef ARMNN_ANDROID_NN_V1_3
249
250inline bool IsBool(V1_3::Operand operand)
251{
252 return operand.type == V1_3::OperandType::BOOL;
253}
254
255/// Checks if a operand is 1_2 Operand
256inline bool Is12OrLaterOperand(V1_3::Operand)
Sadik Armagan61113162019-07-25 09:09:40 +0100257{
258 return true;
259}
260
Mike Kellyb5fdf382019-06-11 16:35:25 +0100261#endif
262
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100263template<typename LayerHandleType>
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +0000264armnn::IConnectableLayer& AddReshapeLayer(armnn::INetwork& network,
265 LayerHandleType& inputLayer,
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100266 armnn::TensorInfo reshapeInfo)
267{
268 armnn::ReshapeDescriptor reshapeDescriptor;
269 reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
270
271 armnn::IConnectableLayer* reshapeLayer = network.AddReshapeLayer(reshapeDescriptor);
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +0100272 ARMNN_ASSERT(reshapeLayer != nullptr);
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100273
274 // Attach the input layer to the reshape layer
275 inputLayer.Connect(reshapeLayer->GetInputSlot(0));
276 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapeInfo);
277
278 return *reshapeLayer;
279}
280
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +0000281bool BroadcastTensor(LayerInputHandle& input0,
282 LayerInputHandle& input1,
283 armnn::IConnectableLayer* startLayer,
284 ConversionData& data)
arovir01b0717b52018-09-05 17:03:25 +0100285{
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +0100286 ARMNN_ASSERT(startLayer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +0100287
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100288 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
289 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
290
291 unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
292 unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
293
294 if (inputDimensions0 == inputDimensions1)
arovir01b0717b52018-09-05 17:03:25 +0100295 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100296 // The inputs have the same number of dimensions, simply connect them to the given layer as they are
297 input0.Connect(startLayer->GetInputSlot(0));
298 input1.Connect(startLayer->GetInputSlot(1));
299
Sadik Armagan64b19b52019-08-19 09:49:58 +0100300 return true;
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100301 }
302
303 // Since the number of dimensions do not match then we need to add degenerate dimensions
304 // to the "smaller" tensor using a reshape, while keeping the order of the inputs.
305
306 unsigned int maxInputDimensions = std::max(inputDimensions0, inputDimensions1);
307 unsigned int sizeDifference = std::abs(boost::numeric_cast<int>(inputDimensions0) -
308 boost::numeric_cast<int>(inputDimensions1));
309
310 bool input0IsSmaller = inputDimensions0 < inputDimensions1;
311 LayerInputHandle& smallInputHandle = input0IsSmaller ? input0 : input1;
312 const armnn::TensorInfo& smallInfo = smallInputHandle.GetTensorInfo();
313
314 const armnn::TensorShape& smallShape = smallInfo.GetShape();
315 std::vector<unsigned int> reshapedDimensions(maxInputDimensions, 1);
316 for (unsigned int i = sizeDifference; i < maxInputDimensions; i++)
317 {
318 reshapedDimensions[i] = smallShape[i - sizeDifference];
319 }
320
321 armnn::TensorInfo reshapedInfo = smallInfo;
322 reshapedInfo.SetShape(armnn::TensorShape{ boost::numeric_cast<unsigned int>(reshapedDimensions.size()),
323 reshapedDimensions.data() });
Sadik Armagan64b19b52019-08-19 09:49:58 +0100324
325 // RehsapeDescriptor that is ignored in the IsReshapeSupported function
326 armnn::ReshapeDescriptor reshapeDescriptor;
327
328 bool isSupported = false;
329 FORWARD_LAYER_SUPPORT_FUNC(__func__,
330 IsReshapeSupported,
331 data.m_Backends,
332 isSupported,
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +0000333 smallInfo,
Sadik Armagan64b19b52019-08-19 09:49:58 +0100334 reshapedInfo,
335 reshapeDescriptor);
336 if (!isSupported)
337 {
338 return false;
339 }
340
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +0100341 ARMNN_ASSERT(data.m_Network != nullptr);
Sadik Armagan64b19b52019-08-19 09:49:58 +0100342 armnn::IConnectableLayer& reshapeLayer = AddReshapeLayer(*data.m_Network, smallInputHandle, reshapedInfo);
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100343
344 if (input0IsSmaller)
345 {
346 // Input0 is the "smaller" tensor, connect the reshape layer as follows:
347 //
348 // Input0 Input1
arovir01b0717b52018-09-05 17:03:25 +0100349 // | |
350 // Reshape |
351 // \ /
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100352 // StartLayer
arovir01b0717b52018-09-05 17:03:25 +0100353
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100354 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
355 input1.Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100356 }
357 else
358 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100359 // Input1 is the "smaller" tensor, connect the reshape layer as follows:
360 //
361 // Input0 Input1
362 // | |
363 // | Reshape
364 // \ /
365 // StartLayer
366
arovir01b0717b52018-09-05 17:03:25 +0100367 input0.Connect(startLayer->GetInputSlot(0));
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100368 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100369 }
Sadik Armagan64b19b52019-08-19 09:49:58 +0100370
371 return true;
arovir01b0717b52018-09-05 17:03:25 +0100372}
373
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +0000374void CalcPadding(uint32_t input,
375 uint32_t kernel,
376 uint32_t stride,
377 uint32_t& outPadHead,
378 uint32_t& outPadTail,
arovir01b0717b52018-09-05 17:03:25 +0100379 android::nn::PaddingScheme scheme)
380{
381 int32_t padHead;
382 int32_t padTail;
383 calculateExplicitPadding(input, stride, kernel, scheme, &padHead, &padTail);
384 outPadHead = boost::numeric_cast<uint32_t>(padHead);
385 outPadTail = boost::numeric_cast<uint32_t>(padTail);
386}
387
Kevin May42477c12020-03-26 13:34:14 +0000388#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3)
Mike Kelly86b36d42019-07-12 16:39:33 +0100389
390void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t dilation, uint32_t& outPadHead,
391 uint32_t& outPadTail, android::nn::PaddingScheme scheme)
392{
393 int32_t padHead;
394 int32_t padTail;
395 calculateExplicitPadding(input, stride, dilation, kernel, scheme, &padHead, &padTail);
396 outPadHead = boost::numeric_cast<uint32_t>(padHead);
397 outPadTail = boost::numeric_cast<uint32_t>(padTail);
398}
399
Mike Kelly26123db2020-01-15 10:02:33 +0000400void CalcPaddingTransposeConv(uint32_t output, uint32_t kernel, int32_t stride, int32_t& outPadHead,
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +0100401 int32_t& outPadTail, android::nn::PaddingScheme scheme)
402{
403 calculateExplicitPaddingTransposeConv(output, stride, kernel, scheme, &outPadHead, &outPadTail);
404}
405
Mike Kelly86b36d42019-07-12 16:39:33 +0100406#endif
407
Matthew Bentham912b3622019-05-03 15:49:14 +0100408Shape GetOperandShape(const V1_0::Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100409{
410 Shape shape;
Matthew Bentham912b3622019-05-03 15:49:14 +0100411 shape.type = OperandType(operand.type);
arovir01b0717b52018-09-05 17:03:25 +0100412 shape.dimensions = operand.dimensions;
413 shape.scale = operand.scale;
414 shape.offset = operand.zeroPoint;
415 return shape;
416}
417
Kevin May42477c12020-03-26 13:34:14 +0000418#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3)
Mike Kelly46272802019-08-14 17:00:48 +0100419
420Shape GetOperandShape(const V1_2::Operand& operand)
421{
422 Shape shape;
423 shape.type = OperandType(operand.type);
424 shape.dimensions = operand.dimensions;
425 shape.scale = operand.scale;
426 shape.offset = operand.zeroPoint;
427 return shape;
428}
429
430#endif
431
Kevin May42477c12020-03-26 13:34:14 +0000432#ifdef ARMNN_ANDROID_NN_V1_3
433
434Shape GetOperandShape(const V1_3::Operand& operand)
435{
436 Shape shape;
437 shape.type = OperandType(operand.type);
438 shape.dimensions = operand.dimensions;
439 shape.scale = operand.scale;
440 shape.offset = operand.zeroPoint;
441 return shape;
442}
443
444#endif
445
arovir01b0717b52018-09-05 17:03:25 +0100446// ArmNN requires the bias scale to be equal to the product of the weight and input scales, which is also
447// 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 +0100448// we accept some tolerance. We don't want ArmNN itself to accept these inconsistencies as it is up to the
449// user (us, in this case) to ensure they match.
arovir01b0717b52018-09-05 17:03:25 +0100450void SanitizeBiasQuantizationScale(armnn::TensorInfo& biasInfo,
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000451 const armnn::TensorInfo& weightInfo,
452 const armnn::TensorInfo& inputInfo)
arovir01b0717b52018-09-05 17:03:25 +0100453{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000454 if (weightInfo.HasPerAxisQuantization())
arovir01b0717b52018-09-05 17:03:25 +0100455 {
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000456 // NOTE: Bias scale is always set to 0 for per-axis quantization and
457 // it needs to be calculated: scale[i] = input_scale * weight_scale[i]
458 auto UpdateBiasScaleValue = [&inputInfo](float biasScale) -> float
arovir01b0717b52018-09-05 17:03:25 +0100459 {
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000460 return biasScale * inputInfo.GetQuantizationScale();
461 };
462
463 std::vector<float> biasScales(weightInfo.GetQuantizationScales());
464 std::transform(biasScales.begin(), biasScales.end(), biasScales.begin(), UpdateBiasScaleValue);
465
466 biasInfo.SetQuantizationScales(biasScales);
467 biasInfo.SetQuantizationDim(weightInfo.GetQuantizationDim());
468
469 ALOGV("Bias quantization params have been updated for per-axis quantization");
470 }
471 else
472 {
473 const float expectedBiasScale = weightInfo.GetQuantizationScale() * inputInfo.GetQuantizationScale();
474 if (biasInfo.GetQuantizationScale() != expectedBiasScale)
475 {
476 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
477 if (comparer(biasInfo.GetQuantizationScale(), expectedBiasScale))
478 {
479 ALOGW("Bias quantization scale has been modified to match input * weights");
480 biasInfo.SetQuantizationScale(expectedBiasScale);
481 }
arovir01b0717b52018-09-05 17:03:25 +0100482 }
483 }
484}
485
486// 4D Tensor Permutations
487const armnn::PermutationVector IdentityPermutation4D({ 0U, 1U, 2U, 3U });
arovir01b0717b52018-09-05 17:03:25 +0100488const armnn::PermutationVector SwapDim1And2({ 0U, 2U, 1U, 3U });
489
490// 3D Permutation Vectors
Mike Kelly4a956582020-02-28 10:32:09 +0000491const armnn::PermutationVector RotateTensorLeft({ 1U, 2U, 0U });
492const armnn::PermutationVector RotateTensorRight({ 2U, 0U, 1U });
arovir01b0717b52018-09-05 17:03:25 +0100493
494template<typename OSlot>
Mike Kelly4a956582020-02-28 10:32:09 +0000495armnn::IConnectableLayer& AddTransposeLayer(armnn::INetwork& network, OSlot& input,
496 const armnn::PermutationVector& mappings)
arovir01b0717b52018-09-05 17:03:25 +0100497{
498 // Add swizzle layer
Mike Kelly4a956582020-02-28 10:32:09 +0000499 armnn::IConnectableLayer* const layer = network.AddTransposeLayer(mappings);
arovir01b0717b52018-09-05 17:03:25 +0100500
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +0100501 ARMNN_ASSERT(layer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +0100502
503 // Connect input to swizzle layer
504 input.Connect(layer->GetInputSlot(0));
505
506 // Setup swizzled output
Mike Kelly4a956582020-02-28 10:32:09 +0000507 const armnn::TensorInfo outInfo = armnnUtils::TransposeTensorShape(input.GetTensorInfo(), mappings);
arovir01b0717b52018-09-05 17:03:25 +0100508 layer->GetOutputSlot(0).SetTensorInfo(outInfo);
509
510 return *layer;
511}
512
arovir01b0717b52018-09-05 17:03:25 +0100513bool ValidateConcatOutputShape(const std::vector<armnn::TensorShape> & inputShapes,
514 const armnn::TensorShape & outputShape,
515 uint32_t concatDim)
516{
517 // Validate the output shape is correct given the input shapes (which have just been validated)
518 unsigned int numDimensions = inputShapes[0].GetNumDimensions();
519 if (outputShape.GetNumDimensions() != numDimensions)
520 {
521 return Fail("%s: Output shape has wrong number of dimensions", __func__);
522 }
523
524 unsigned int outputSizeAlongConcatenatedDimension = 0;
525 for (unsigned int i = 0; i < inputShapes.size(); i++)
526 {
527 outputSizeAlongConcatenatedDimension += inputShapes[i][concatDim];
528 }
529
530 for (unsigned int i = 0; i < numDimensions; ++i)
531 {
532 if (i == concatDim)
533 {
534 if (outputShape[i] != outputSizeAlongConcatenatedDimension)
535 {
536 return Fail(
537 "%s: Invalid output shape for dimension %d (%d != %d)",
538 __func__,
539 i,
540 outputShape[i],
541 outputSizeAlongConcatenatedDimension);
542 }
543 }
544 else
545 {
546 if (outputShape[i] != inputShapes[0][i])
547 {
548 return Fail("%s: Invalid output shape", __func__);
549 }
550 }
551 }
552
553 return true;
554}
555
556bool RequiresReshape(armnn::TensorShape & inputShape)
557{
558 return inputShape.GetNumDimensions() < 3;
559}
560
arovir01b0717b52018-09-05 17:03:25 +0100561void SwizzleInputs(armnn::INetwork& network,
562 std::vector<LayerInputHandle>& inputs,
563 std::vector<armnn::TensorShape>& inputShapes,
564 const armnn::PermutationVector& mapping)
565{
566 if (!mapping.IsEqual(IdentityPermutation4D))
567 {
568 size_t nInputs = inputs.size();
569 for (size_t i=0; i<nInputs; ++i)
570 {
571 // add swizzle layer
Mike Kelly4a956582020-02-28 10:32:09 +0000572 armnn::IConnectableLayer& swizzleLayer = AddTransposeLayer(network, inputs[i], mapping);
arovir01b0717b52018-09-05 17:03:25 +0100573 auto& outputSlot = swizzleLayer.GetOutputSlot(0);
574 auto& outputInfo = outputSlot.GetTensorInfo();
575 // replace inputs with the swizzled ones
576 inputs[i] = LayerInputHandle(true, &outputSlot, outputInfo);
577 inputShapes[i] = inputs[i].GetTensorInfo().GetShape();
578 }
579 }
580}
581
Kevin Mayaed08ac2019-12-12 16:33:31 +0000582bool CheckReshapeSupported(ConversionData& data,
583 std::vector<LayerInputHandle>& inputs,
584 std::vector<armnn::TensorShape>& inputShapes,
585 const armnn::PermutationVector& mapping,
586 const armnn::TensorInfo& outputInfo)
587{
588 if (!mapping.IsEqual(IdentityPermutation4D))
589 {
590 size_t nInputs = inputs.size();
591 for (size_t i=0; i<nInputs; ++i)
592 {
593 // check permute layer
Mike Kelly4a956582020-02-28 10:32:09 +0000594 armnn::TransposeDescriptor transposeDesc;
595 transposeDesc.m_DimMappings = mapping;
Kevin Mayaed08ac2019-12-12 16:33:31 +0000596
597 bool isSupported = false;
598 FORWARD_LAYER_SUPPORT_FUNC(__func__,
Mike Kelly4a956582020-02-28 10:32:09 +0000599 IsTransposeSupported,
Kevin Mayaed08ac2019-12-12 16:33:31 +0000600 data.m_Backends,
601 isSupported,
602 inputs[i].GetTensorInfo(),
603 outputInfo,
Mike Kelly4a956582020-02-28 10:32:09 +0000604 transposeDesc);
Kevin Mayaed08ac2019-12-12 16:33:31 +0000605 if (!isSupported)
606 {
607 return false;
608 }
609
610 }
611 SwizzleInputs(*data.m_Network, inputs, inputShapes, mapping);
612 }
613 return true;
614}
615
616
narpra01f176d5a2018-11-18 20:17:48 +0000617bool CreateConcatPermutationParameters(const unsigned int numberOfDimensions,
618 int32_t & concatDimension,
619 std::pair<armnn::PermutationVector, armnn::PermutationVector> & permutationPair)
arovir01b0717b52018-09-05 17:03:25 +0100620{
narpra01f176d5a2018-11-18 20:17:48 +0000621 bool needPermute = false;
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +0100622 ARMNN_ASSERT(numberOfDimensions >= 3);
arovir01b0717b52018-09-05 17:03:25 +0100623
624 // ArmNN uses Compute Library subtensors to perform concatenation
narpra01f176d5a2018-11-18 20:17:48 +0000625 // This only works when concatenating along dimension 0, 1 or 3 for a 4-D tensor,
626 // or along dimension 0 or 2 for a 3-D tensor.
627 if (numberOfDimensions == 4 && concatDimension == 2)
arovir01b0717b52018-09-05 17:03:25 +0100628 {
narpra01f176d5a2018-11-18 20:17:48 +0000629 concatDimension = 1;
630 permutationPair = std::make_pair(SwapDim1And2, SwapDim1And2);
631 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100632 }
narpra01f176d5a2018-11-18 20:17:48 +0000633 else if (numberOfDimensions == 3 && concatDimension == 1)
arovir01b0717b52018-09-05 17:03:25 +0100634 {
narpra01f176d5a2018-11-18 20:17:48 +0000635 concatDimension = 0;
636 permutationPair = std::make_pair(RotateTensorLeft, RotateTensorRight);
637 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100638 }
narpra01f176d5a2018-11-18 20:17:48 +0000639 return needPermute;
arovir01b0717b52018-09-05 17:03:25 +0100640}
641
642} // anonymous namespace
643
644namespace armnn_driver
645{
646
647//// Creates an ArmNN activation layer and connects it to the given layer, if the
648//// passed in AndroidNN activation function requires so.
649//// @return The end layer of the sequence of layers built for the given AndroidNN
650//// activation function or nullptr if an error occurred (e.g. unsupported activation).
651//// Note that the end layer matches the input layer if no activation is required
652//// (the sequence of layers has length 1).
653armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
654 ActivationFn activation,
655 armnn::IConnectableLayer* prevLayer,
656 ConversionData& data);
657
658} // namespace armnn_driver
659
660///
661/// Utility templates
662///
663
664namespace armnn_driver
665{
666
667using namespace android::nn;
668
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100669template<typename HalPolicy,
670 typename HalOperand = typename HalPolicy::Operand,
671 typename HalOperation = typename HalPolicy::Operation,
672 typename HalModel = typename HalPolicy::Model>
673const HalOperand* GetInputOperand(const HalOperation& operation,
674 uint32_t inputIndex,
675 const HalModel& model,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100676 bool failOnIndexOutOfBounds = true)
arovir01b0717b52018-09-05 17:03:25 +0100677{
678 if (inputIndex >= operation.inputs.size())
679 {
saoste01b8471482018-10-10 09:44:51 +0100680 if (failOnIndexOutOfBounds)
681 {
682 Fail("%s: invalid input index: %i out of %i", __func__, inputIndex, operation.inputs.size());
683 }
arovir01b0717b52018-09-05 17:03:25 +0100684 return nullptr;
685 }
686
Kevin May42477c12020-03-26 13:34:14 +0000687 // Model should have been validated beforehand
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +0100688 ARMNN_ASSERT(operation.inputs[inputIndex] < getMainModel(model).operands.size());
Kevin May42477c12020-03-26 13:34:14 +0000689 return &getMainModel(model).operands[operation.inputs[inputIndex]];
arovir01b0717b52018-09-05 17:03:25 +0100690}
691
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100692template<typename HalPolicy,
693 typename HalOperand = typename HalPolicy::Operand,
694 typename HalOperation = typename HalPolicy::Operation,
695 typename HalModel = typename HalPolicy::Model>
696const HalOperand* GetOutputOperand(const HalOperation& operation,
697 uint32_t outputIndex,
698 const HalModel& model)
arovir01b0717b52018-09-05 17:03:25 +0100699{
700 if (outputIndex >= operation.outputs.size())
701 {
702 Fail("%s: invalid output index: %i out of %i", __func__, outputIndex, operation.outputs.size());
703 return nullptr;
704 }
705
706 // Model should have been validated beforehand
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +0100707 ARMNN_ASSERT(operation.outputs[outputIndex] < getMainModel(model).operands.size());
arovir01b0717b52018-09-05 17:03:25 +0100708
Kevin May42477c12020-03-26 13:34:14 +0000709 return &getMainModel(model).operands[operation.outputs[outputIndex]];
arovir01b0717b52018-09-05 17:03:25 +0100710}
711
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100712template<typename HalPolicy,
Pablo Tellofb45e2f2019-10-18 16:51:57 +0100713 typename HalOperand = typename HalPolicy::Operand,
714 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100715const void* GetOperandValueReadOnlyAddress(const HalOperand& operand,
Matthew Bentham912b3622019-05-03 15:49:14 +0100716 const HalModel& model,
717 const ConversionData& data,
Kevin Mayf29a2c52019-03-14 11:56:32 +0000718 bool optional = false)
arovir01b0717b52018-09-05 17:03:25 +0100719{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100720 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
arovir01b0717b52018-09-05 17:03:25 +0100721
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100722 const void* valueStart = nullptr;
arovir01b0717b52018-09-05 17:03:25 +0100723 switch (operand.lifetime)
724 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100725 case HalOperandLifeTime::CONSTANT_COPY:
arovir01b0717b52018-09-05 17:03:25 +0100726 {
727 // Constant found in model.operandValues
728 valueStart = &model.operandValues[operand.location.offset];
729 break;
730 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100731 case HalOperandLifeTime::CONSTANT_REFERENCE:
arovir01b0717b52018-09-05 17:03:25 +0100732 {
733 // Constant specified via a Memory object
734 valueStart = GetMemoryFromPool(operand.location, data.m_MemPools);
735 break;
736 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100737 case HalOperandLifeTime::NO_VALUE:
Kevin Mayf29a2c52019-03-14 11:56:32 +0000738 {
739 // An optional input tensor with no values is not an error so should not register as a fail
740 if (optional)
741 {
742 valueStart = nullptr;
743 break;
744 }
Matthew Bentham912b3622019-05-03 15:49:14 +0100745 [[fallthrough]];
Kevin Mayf29a2c52019-03-14 11:56:32 +0000746 }
arovir01b0717b52018-09-05 17:03:25 +0100747 default:
748 {
749 // Unsupported/invalid (e.g. can't get value of an input to the model)
750 Fail("%s: unsupported/invalid operand lifetime: %s",
751 __func__, toString(operand.lifetime).c_str());
752 valueStart = nullptr;
753 }
754 }
755
756 return valueStart;
757}
758
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100759template<typename HalPolicy,
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100760 typename HalOperation = typename HalPolicy::Operation,
761 typename HalModel = typename HalPolicy::Model,
762 typename HalOperandType = typename HalPolicy::OperandType>
763bool GetOperandType(const HalOperation& operation,
764 uint32_t inputIndex,
765 const HalModel& model,
766 HalOperandType& type)
767{
768 using HalOperand = typename HalPolicy::Operand;
769
770 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
771 if (!operand)
772 {
773 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
774 }
775
776 type = operand->type;
777 return true;
778}
779
780template<typename HalPolicy,
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000781 typename HalOperand = typename HalPolicy::Operand>
782bool IsOperandConstant(const HalOperand& operand)
783{
784 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
785
786 HalOperandLifeTime lifetime = operand.lifetime;
787
788 return lifetime == HalOperandLifeTime::CONSTANT_COPY ||
789 lifetime == HalOperandLifeTime::CONSTANT_REFERENCE ||
790 lifetime == HalOperandLifeTime::NO_VALUE;
791}
792
793template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100794 typename HalOperand = typename HalPolicy::Operand,
795 typename HalModel = typename HalPolicy::Model>
796ConstTensorPin ConvertOperandToConstTensorPin(const HalOperand& operand,
797 const HalModel& model,
798 const ConversionData& data,
799 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
800 const armnn::TensorShape* overrideTensorShape = nullptr,
801 bool optional = false)
802{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100803 if (!IsOperandTypeSupportedForTensors(operand.type))
804 {
805 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand.type).c_str());
806 return ConstTensorPin();
807 }
808
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000809 if (!optional && !IsOperandConstant<HalPolicy>(operand))
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100810 {
811 Fail("%s: invalid operand lifetime: %s", __func__, toString(operand.lifetime).c_str());
812 return ConstTensorPin();
813 }
814
815 const void* const valueStart = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data, optional);
816 if (!valueStart)
817 {
818 if (optional)
819 {
820 // optional tensor with no values is not really an error; return it as invalid, but marked as optional
821 return ConstTensorPin(true);
822 }
823 // mandatory tensor with no values
824 Fail("%s: failed to get operand address", __func__);
825 return ConstTensorPin();
826 }
827
828 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(operand);
Teresa Charlin02dce092019-11-11 17:06:23 +0000829 // Android datalayout might be different than armnn datalayout, e.g. the kernel for the depthwise convolution.
830 if (tensorInfo.HasPerAxisQuantization())
831 {
832 tensorInfo.SetQuantizationDim(dimensionMappings[tensorInfo.GetQuantizationDim().value()]);
833 }
834
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100835 if (overrideTensorShape != nullptr)
836 {
837 tensorInfo.SetShape(*overrideTensorShape);
838 }
839 return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings);
840}
841
842template<typename HalPolicy,
843 typename HalOperation = typename HalPolicy::Operation,
844 typename HalModel = typename HalPolicy::Model>
845ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operation,
846 uint32_t inputIndex,
847 const HalModel& model,
848 const ConversionData& data,
849 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
850 const armnn::TensorShape* overrideTensorShape = nullptr,
851 bool optional = false)
852{
853 using HalOperand = typename HalPolicy::Operand;
854
855 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
856 if (!operand)
857 {
858 Fail("%s: failed to get input operand: index=%u", __func__, inputIndex);
859 return ConstTensorPin();
860 }
861 return ConvertOperandToConstTensorPin<HalPolicy>(*operand,
862 model,
863 data,
864 dimensionMappings,
865 overrideTensorShape,
866 optional);
867}
868
869template<typename HalPolicy,
870 typename OutputType,
871 typename HalOperandType = typename HalPolicy::OperandType,
872 typename HalOperation = typename HalPolicy::Operation,
873 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100874bool GetInputScalar(const HalOperation& operation,
875 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100876 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100877 OutputType& outValue,
878 const HalModel& model,
879 const ConversionData& data)
880{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100881 using HalOperand = typename HalPolicy::Operand;
882
883 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +0100884 if (!operand)
885 {
886 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
887 }
888
889 if (operand->type != type)
890 {
891 return Fail("%s: unexpected operand type: %s (should be %s)",
892 __func__, toString(operand->type).c_str(), toString(type).c_str());
893 }
894
895 if (operand->location.length != sizeof(OutputType))
896 {
897 return Fail("%s: incorrect operand location length: %i (should be %i)",
898 __func__, operand->location.length, sizeof(OutputType));
899 }
900
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100901 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100902 if (!valueAddress)
903 {
904 return Fail("%s: failed to get address for operand", __func__);
905 }
906
907 outValue = *(static_cast<const OutputType*>(valueAddress));
908 return true;
909}
910
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100911template<typename HalPolicy,
912 typename HalOperation = typename HalPolicy::Operation,
913 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100914bool GetInputInt32(const HalOperation& operation,
915 uint32_t inputIndex,
916 int32_t& outValue,
917 const HalModel& model,
918 const ConversionData& data)
919{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100920 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::INT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100921}
922
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100923template<typename HalPolicy,
924 typename HalOperation = typename HalPolicy::Operation,
925 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100926bool GetInputFloat32(const HalOperation& operation,
927 uint32_t inputIndex,
928 float& outValue,
929 const HalModel& model,
930 const ConversionData& data)
931{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100932 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::FLOAT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100933}
934
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100935template<typename HalPolicy,
936 typename HalOperation = typename HalPolicy::Operation,
937 typename HalOperandType = typename HalPolicy::OperandType,
938 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100939bool GetInputActivationFunctionImpl(const HalOperation& operation,
940 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100941 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100942 ActivationFn& outActivationFunction,
943 const HalModel& model,
944 const ConversionData& data)
945{
Mike Kellyb5fdf382019-06-11 16:35:25 +0100946 if (type != HalOperandType::INT32 && type != HalOperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100947 {
948 return Fail("%s: unexpected operand type: %s (should be %s or %s)",
949 __func__,
950 toString(type).c_str(),
951 toString(OperandType::INT32).c_str(),
952 toString(OperandType::TENSOR_INT32).c_str());
953 }
954
955 int32_t activationFunctionAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100956 if (!GetInputScalar<HalPolicy>(operation, inputIndex, type, activationFunctionAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100957 {
958 return Fail("%s: failed to get activation input value", __func__);
959 }
960 outActivationFunction = static_cast<ActivationFn>(activationFunctionAsInt);
961 return true;
962}
963
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100964template<typename HalPolicy,
965 typename HalOperation = typename HalPolicy::Operation,
966 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100967bool GetInputActivationFunction(const HalOperation& operation,
968 uint32_t inputIndex,
969 ActivationFn& outActivationFunction,
970 const HalModel& model,
971 const ConversionData& data)
972{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100973 return GetInputActivationFunctionImpl<HalPolicy>(operation,
974 inputIndex,
975 HalPolicy::OperandType::INT32,
976 outActivationFunction,
977 model,
978 data);
arovir01b0717b52018-09-05 17:03:25 +0100979}
980
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100981template<typename HalPolicy,
982 typename HalOperation = typename HalPolicy::Operation,
983 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100984bool GetInputActivationFunctionFromTensor(const HalOperation& operation,
985 uint32_t inputIndex,
986 ActivationFn& outActivationFunction,
987 const HalModel& model,
988 const ConversionData& data)
989{
990 // This only accepts a 1-D tensor of size 1
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100991 return GetInputActivationFunctionImpl<HalPolicy>(operation,
992 inputIndex,
993 HalPolicy::OperandType::INT32,
994 outActivationFunction,
995 model,
996 data);
arovir01b0717b52018-09-05 17:03:25 +0100997}
998
999
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001000template<typename HalPolicy,
1001 typename HalOperation = typename HalPolicy::Operation,
1002 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001003bool GetOptionalInputActivation(const HalOperation& operation,
1004 uint32_t inputIndex,
1005 ActivationFn& activationFunction,
1006 const HalModel& model,
1007 const ConversionData& data)
1008{
1009 if (operation.inputs.size() <= inputIndex)
1010 {
1011 activationFunction = ActivationFn::kActivationNone;
1012 }
1013 else
1014 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001015 if (!GetInputActivationFunction<HalPolicy>(operation, inputIndex, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001016 {
1017 return Fail("%s: Operation has invalid inputs", __func__);
1018 }
1019 }
1020 return true;
1021}
1022
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001023template<typename HalPolicy,
1024 typename ConvolutionDescriptor,
1025 typename HalOperation = typename HalPolicy::Operation,
1026 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +01001027bool GetOptionalConvolutionDilationParams(const HalOperation& operation,
1028 uint32_t dilationXIndex,
1029 ConvolutionDescriptor& descriptor,
1030 const HalModel& model,
1031 const ConversionData& data)
1032{
1033 bool success = true;
1034 if (operation.inputs.size() >= dilationXIndex + 2)
1035 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001036 success &= GetInputScalar<HalPolicy>(operation,
1037 dilationXIndex,
1038 HalPolicy::OperandType::INT32,
1039 descriptor.m_DilationX,
1040 model,
1041 data);
1042 success &= GetInputScalar<HalPolicy>(operation,
1043 dilationXIndex + 1,
1044 HalPolicy::OperandType::INT32,
1045 descriptor.m_DilationY,
1046 model,
1047 data);
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +01001048 }
1049
1050 return success;
1051}
1052
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001053template<typename HalPolicy,
1054 typename HalOperand = typename HalPolicy::Operand,
1055 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001056bool GetTensorInt32Values(const HalOperand& operand,
arovir01b0717b52018-09-05 17:03:25 +01001057 std::vector<int32_t>& outValues,
1058 const HalModel& model,
1059 const ConversionData& data)
1060{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001061 if (operand.type != HalPolicy::OperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +01001062 {
1063 return Fail("%s: invalid operand type: %s", __func__, toString(operand.type).c_str());
1064 }
1065
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001066 const void* startAddress = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001067 if (!startAddress)
1068 {
1069 return Fail("%s: failed to get operand address", __func__, operand.type);
1070 }
1071
1072 // Check number of bytes is sensible
1073 const uint32_t numBytes = operand.location.length;
1074 if (numBytes % sizeof(int32_t) != 0)
1075 {
1076 return Fail("%s: invalid number of bytes: %i, expected to be a multiple of %i",
1077 __func__, numBytes, sizeof(int32_t));
1078 }
1079
1080 outValues.resize(numBytes / sizeof(int32_t));
1081 memcpy(outValues.data(), startAddress, numBytes);
1082 return true;
1083}
1084
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001085template<typename HalPolicy,
1086 typename HalOperation = typename HalPolicy::Operation,
1087 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001088bool GetInputPaddingScheme(const HalOperation& operation,
1089 uint32_t inputIndex,
1090 PaddingScheme& outPaddingScheme,
1091 const HalModel& model,
1092 const ConversionData& data)
1093{
1094 int32_t paddingSchemeAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001095 if (!GetInputInt32<HalPolicy>(operation, inputIndex, paddingSchemeAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001096 {
1097 return Fail("%s: failed to get padding scheme input value", __func__);
1098 }
1099
1100 outPaddingScheme = static_cast<android::nn::PaddingScheme>(paddingSchemeAsInt);
1101 return true;
1102}
1103
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001104template<typename HalPolicy,
1105 typename HalOperation = typename HalPolicy::Operation,
1106 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001107LayerInputHandle ConvertToLayerInputHandle(const HalOperation& operation,
1108 uint32_t inputIndex,
1109 const HalModel& model,
1110 ConversionData& data)
1111{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001112 using HalOperand = typename HalPolicy::Operand;
Sadik Armagan44bcc022019-06-18 17:21:36 +01001113 using HalOperandType = typename HalPolicy::OperandType;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001114 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
1115
1116 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +01001117 if (!operand)
1118 {
1119 Fail("%s: failed to get input operand %i", __func__, inputIndex);
1120 return LayerInputHandle();
1121 }
1122
1123 if (!IsOperandTypeSupportedForTensors(operand->type))
1124 {
1125 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand->type).c_str());
1126 return LayerInputHandle();
1127 }
1128
Sadik Armagan44bcc022019-06-18 17:21:36 +01001129 try
arovir01b0717b52018-09-05 17:03:25 +01001130 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001131 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001132 if (IsDynamicTensor(operandTensorInfo))
1133 {
1134 Fail("%s: dynamic input tensors are not supported", __func__);
1135 return LayerInputHandle();
1136 }
arovir01b0717b52018-09-05 17:03:25 +01001137
Sadik Armagan44bcc022019-06-18 17:21:36 +01001138 switch (operand->lifetime)
arovir01b0717b52018-09-05 17:03:25 +01001139 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001140 case HalOperandLifeTime::MODEL_INPUT:
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001141 {
1142 // NOTE: We must check whether we can support the input tensor on at least one
1143 // of the provided backends; otherwise we cannot convert the operation
1144 bool isInputSupported = false;
1145 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1146 IsInputSupported,
1147 data.m_Backends,
1148 isInputSupported,
1149 operandTensorInfo);
1150
1151 if (!isInputSupported)
1152 {
1153 Fail("%s: unsupported input tensor", __func__);
1154 return LayerInputHandle();
1155 }
1156
1157 BOOST_FALLTHROUGH; // intentional fallthrough
1158 }
1159 case HalOperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001160 case HalOperandLifeTime::MODEL_OUTPUT:
arovir01b0717b52018-09-05 17:03:25 +01001161 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001162 // The tensor is either an operand internal to the model, or a model input.
1163 // It can be associated with an ArmNN output slot for an existing layer.
1164
1165 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
1166 const uint32_t operandIndex = operation.inputs[inputIndex];
1167 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
Sadik Armagan44bcc022019-06-18 17:21:36 +01001168 }
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001169 case HalOperandLifeTime::CONSTANT_COPY: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001170 case HalOperandLifeTime::CONSTANT_REFERENCE:
1171 {
1172 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
1173 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin<HalPolicy>(*operand, model, data);
1174 if (tensorPin.IsValid())
arovir01b0717b52018-09-05 17:03:25 +01001175 {
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001176 bool isSupported = false;
1177 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1178 IsConstantSupported,
1179 data.m_Backends,
1180 isSupported,
1181 tensorPin.GetConstTensor().GetInfo());
Mike Kelly28e3d9f2019-08-07 14:55:04 +01001182 if (!isSupported)
Sadik Armagan44bcc022019-06-18 17:21:36 +01001183 {
1184 return LayerInputHandle();
1185 }
1186
1187 armnn::IConnectableLayer* constantLayer =
1188 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
1189 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
1190 outputSlot.SetTensorInfo(tensorPin.GetConstTensor().GetInfo());
1191
1192 return LayerInputHandle(true, &outputSlot, operandTensorInfo);
1193 }
1194 else
1195 {
1196 Fail("%s: invalid operand tensor", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001197 return LayerInputHandle();
1198 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001199 break;
arovir01b0717b52018-09-05 17:03:25 +01001200 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001201 default:
arovir01b0717b52018-09-05 17:03:25 +01001202 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001203 // Unsupported lifetime for an input tensor
1204 Fail("%s: unsupported lifetime for input tensor: %s",
1205 __func__, toString(operand->lifetime).c_str());
arovir01b0717b52018-09-05 17:03:25 +01001206 return LayerInputHandle();
1207 }
arovir01b0717b52018-09-05 17:03:25 +01001208 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001209 }
1210 catch (UnsupportedOperand<HalOperandType>& e)
1211 {
1212 Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str());
1213 return LayerInputHandle();
arovir01b0717b52018-09-05 17:03:25 +01001214 }
1215}
1216
Kevin May42477c12020-03-26 13:34:14 +00001217
1218#ifdef ARMNN_ANDROID_NN_V1_3
1219template<typename HalPolicy>
1220LayerInputHandle ConvertToLayerInputHandle(const ::android::hardware::neuralnetworks::V1_3::Operation& operation,
1221 uint32_t inputIndex,
1222 const::android::hardware::neuralnetworks::V1_3::Model& model,
1223 ConversionData& data)
1224{
1225 using HalOperand = typename HalPolicy::Operand;
1226 using HalOperandType = typename HalPolicy::OperandType;
1227 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
1228
1229 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
1230 if (!operand)
1231 {
1232 Fail("%s: failed to get input operand %i", __func__, inputIndex);
1233 return LayerInputHandle();
1234 }
1235
1236 if (!IsOperandTypeSupportedForTensors(operand->type))
1237 {
1238 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand->type).c_str());
1239 return LayerInputHandle();
1240 }
1241
1242 try
1243 {
1244 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
1245 if (IsDynamicTensor(operandTensorInfo))
1246 {
1247 Fail("%s: dynamic input tensors are not supported", __func__);
1248 return LayerInputHandle();
1249 }
1250
1251 switch (operand->lifetime)
1252 {
1253 case HalOperandLifeTime::SUBGRAPH_INPUT:
1254 {
1255 // NOTE: We must check whether we can support the input tensor on at least one
1256 // of the provided backends; otherwise we cannot convert the operation
1257 bool isInputSupported = false;
1258 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1259 IsInputSupported,
1260 data.m_Backends,
1261 isInputSupported,
1262 operandTensorInfo);
1263
1264 if (!isInputSupported)
1265 {
1266 Fail("%s: unsupported input tensor", __func__);
1267 return LayerInputHandle();
1268 }
1269
1270 BOOST_FALLTHROUGH; // intentional fallthrough
1271 }
1272 case HalOperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
1273 case HalOperandLifeTime::SUBGRAPH_OUTPUT:
1274 {
1275 // The tensor is either an operand internal to the model, or a model input.
1276 // It can be associated with an ArmNN output slot for an existing layer.
1277
1278 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
1279 const uint32_t operandIndex = operation.inputs[inputIndex];
1280 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
1281 }
1282 case HalOperandLifeTime::CONSTANT_COPY: // intentional fallthrough
1283 case HalOperandLifeTime::CONSTANT_REFERENCE:
1284 {
1285 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
1286 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin<HalPolicy>(*operand, model, data);
1287 if (tensorPin.IsValid())
1288 {
1289 bool isSupported = false;
1290 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1291 IsConstantSupported,
1292 data.m_Backends,
1293 isSupported,
1294 tensorPin.GetConstTensor().GetInfo());
1295 if (!isSupported)
1296 {
1297 return LayerInputHandle();
1298 }
1299
1300 armnn::IConnectableLayer* constantLayer =
1301 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
1302 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
1303 outputSlot.SetTensorInfo(tensorPin.GetConstTensor().GetInfo());
1304
1305 return LayerInputHandle(true, &outputSlot, operandTensorInfo);
1306 }
1307 else
1308 {
1309 Fail("%s: invalid operand tensor", __func__);
1310 return LayerInputHandle();
1311 }
1312 break;
1313 }
1314 default:
1315 {
1316 // Unsupported lifetime for an input tensor
1317 Fail("%s: unsupported lifetime for input tensor: %s",
1318 __func__, toString(operand->lifetime).c_str());
1319 return LayerInputHandle();
1320 }
1321 }
1322 }
1323 catch (UnsupportedOperand<HalOperandType>& e)
1324 {
1325 Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str());
1326 return LayerInputHandle();
1327 }
1328}
1329#endif
1330
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001331template<typename HalPolicy,
1332 typename HalOperation = typename HalPolicy::Operation,
1333 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001334bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1335 uint32_t operationOutputIndex,
1336 armnn::IConnectableLayer& layer,
1337 uint32_t layerOutputIndex,
1338 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001339 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001340{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001341 using HalOperand = typename HalPolicy::Operand;
1342
1343 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001344 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
1345 {
1346 return false;
1347 }
1348
1349 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
1350
1351 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
1352 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
1353
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001354 outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand));
Mike Kellyb5fdf382019-06-11 16:35:25 +01001355
1356 return true;
1357}
1358
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001359template<typename HalPolicy,
1360 typename HalOperation = typename HalPolicy::Operation,
1361 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001362armnn::DataLayout OptionalDataLayout(const HalOperation& operation,
1363 uint32_t inputIndex,
1364 const HalModel& model,
1365 ConversionData& data)
1366{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001367 using HalOperand = typename HalPolicy::Operand;
1368
1369 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001370 if (!operand)
1371 {
1372 return armnn::DataLayout::NHWC;
1373 }
1374
1375 if (!IsBool(*operand))
1376 {
1377 return armnn::DataLayout::NHWC;
1378 }
1379
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001380 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001381 if (!valueAddress)
1382 {
1383 return armnn::DataLayout::NHWC;
1384 }
1385
1386 if (*(static_cast<const bool*>(valueAddress)))
1387 {
1388 return armnn::DataLayout::NCHW;
1389 }
1390 else
1391 {
1392 return armnn::DataLayout::NHWC;
1393 }
1394}
1395
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001396template<typename HalPolicy,
1397 typename HalOperation = typename HalPolicy::Operation,
1398 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001399bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1400 uint32_t outputIndex,
1401 armnn::IConnectableLayer& layer,
1402 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001403 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001404{
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001405 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1406 outputIndex,
1407 layer,
1408 outputIndex,
1409 model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001410 data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001411}
1412
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001413template<typename HalPolicy,
1414 typename HalOperation = typename HalPolicy::Operation,
1415 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001416bool ConvertToActivation(const HalOperation& operation,
1417 const char* operationName,
1418 const armnn::ActivationDescriptor& activationDesc,
1419 const HalModel& model,
1420 ConversionData& data)
1421{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001422 using HalOperand = typename HalPolicy::Operand;
1423
1424 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001425 if (!input.IsValid())
1426 {
1427 return Fail("%s: Input 0 is invalid", operationName);
1428 }
1429
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001430 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001431 if (!outputOperand)
1432 {
1433 return false;
1434 }
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001435
1436 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan2050c232019-07-23 16:59:58 +01001437 if (IsDynamicTensor(outInfo))
1438 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001439 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan2050c232019-07-23 16:59:58 +01001440 }
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001441
1442 bool isSupported = false;
1443 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1444 IsActivationSupported,
1445 data.m_Backends,
1446 isSupported,
1447 input.GetTensorInfo(),
1448 outInfo,
1449 activationDesc);
1450 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001451 {
1452 return false;
1453 }
1454
1455 armnn::IConnectableLayer* layer = data.m_Network->AddActivationLayer(activationDesc);
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +01001456 ARMNN_ASSERT(layer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +01001457 input.Connect(layer->GetInputSlot(0));
1458
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001459 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001460}
1461
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001462template<typename HalPolicy,
Sadik Armagan61113162019-07-25 09:09:40 +01001463 typename HalOperation = typename HalPolicy::Operation,
1464 typename HalModel = typename HalPolicy::Model>
1465bool ConvertReLu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1466{
1467 armnn::ActivationDescriptor desc;
1468 desc.m_Function = armnn::ActivationFunction::ReLu;
1469
1470 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1471}
1472
1473template<typename HalPolicy,
1474 typename HalOperation = typename HalPolicy::Operation,
1475 typename HalModel = typename HalPolicy::Model>
1476bool ConvertReLu1(const HalOperation& operation, const HalModel& model, ConversionData& data)
1477{
1478 armnn::ActivationDescriptor desc;
1479 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1480 desc.m_A = 1.0f;
1481 desc.m_B = -1.0f;
1482
1483 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1484}
1485
1486template<typename HalPolicy,
1487 typename HalOperation = typename HalPolicy::Operation,
1488 typename HalModel = typename HalPolicy::Model>
1489bool ConvertReLu6(const HalOperation& operation, const HalModel& model, ConversionData& data)
1490{
1491 armnn::ActivationDescriptor desc;
1492 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1493 desc.m_A = 6.0f;
1494
1495 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1496}
1497
1498template<typename HalPolicy,
1499 typename HalOperation = typename HalPolicy::Operation,
1500 typename HalModel = typename HalPolicy::Model>
1501bool ConvertTanH(const HalOperation& operation, const HalModel& model, ConversionData& data)
1502{
1503 armnn::ActivationDescriptor desc;
1504 desc.m_Function = armnn::ActivationFunction::TanH;
1505 desc.m_A = 1.0f; // android nn does not support tanH parameters
1506 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1507
1508 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1509}
1510
1511template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001512 typename HalOperation = typename HalPolicy::Operation,
1513 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001514bool ConvertPaddings(const HalOperation& operation,
1515 const HalModel& model,
1516 ConversionData& data,
1517 unsigned int rank,
1518 armnn::PadDescriptor& padDescriptor)
1519{
1520 using HalOperand = typename HalPolicy::Operand;
1521
1522 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
1523 if (!paddingsOperand)
1524 {
1525 return Fail("%s: Could not read paddings operand", __func__);
1526 }
1527
1528 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
1529 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2)
1530 {
1531 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
1532 }
1533
1534 std::vector<int32_t> paddings;
Mike Kellyeec836e2020-02-18 10:03:30 +00001535 if (!GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data))
1536 {
1537 return Fail("%s: Operation has invalid or unsupported paddings operand", __func__);
1538 }
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001539
1540 // add padding for each dimension of input tensor.
1541 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
1542 {
1543 int paddingBeforeInput = paddings[i];
1544 int paddingAfterInput = paddings[i + 1];
1545
1546 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
1547 {
1548 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
1549 }
1550
1551 padDescriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
1552 }
1553
1554 return true;
1555}
1556
1557template<typename HalPolicy,
1558 typename HalOperation = typename HalPolicy::Operation,
1559 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001560bool ConvertPooling2d(const HalOperation& operation,
1561 const char* operationName,
1562 armnn::PoolingAlgorithm poolType,
1563 const HalModel& model,
1564 ConversionData& data)
1565{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001566 using HalOperand = typename HalPolicy::Operand;
1567 using HalOperandType = typename HalPolicy::OperandType;
1568
1569 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001570 if (!input.IsValid())
1571 {
FinnWilliamsArm493e9b72019-11-25 16:02:07 +00001572 return Fail("%s: Operation Could not read input 0", operationName);
arovir01b0717b52018-09-05 17:03:25 +01001573 }
1574
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001575 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001576 if (!output)
1577 {
1578 return Fail("%s: Could not read output 0", __func__);
1579 }
1580
1581 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1582 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1583
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001584 if (IsDynamicTensor(outputInfo))
1585 {
1586 return Fail("%s: Dynamic output tensors are not supported", __func__);
1587 }
1588
arovir01b0717b52018-09-05 17:03:25 +01001589 armnn::Pooling2dDescriptor desc;
1590 desc.m_PoolType = poolType;
1591 desc.m_OutputShapeRounding = armnn::OutputShapeRounding::Floor;
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001592 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001593
1594 ActivationFn activation;
1595
Sadik Armagan15d63e22019-07-26 16:59:35 +01001596 auto inputSize = operation.inputs.size();
1597
1598 if (inputSize >= 10)
1599 {
1600 // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type)
1601 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1602 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1603 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1604 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1605 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1606 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1607 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1608 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1609 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
1610 {
1611 return Fail("%s: Operation has invalid inputs", operationName);
1612 }
1613
Kevin May42477c12020-03-26 13:34:14 +00001614 if (Is12OrLaterOperand(*output))
Sadik Armagan15d63e22019-07-26 16:59:35 +01001615 {
1616 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
1617 }
1618 }
1619 else
arovir01b0717b52018-09-05 17:03:25 +01001620 {
1621 // one input, 6 parameters (padding, stridex, stridey, width, height, activation type)
1622 android::nn::PaddingScheme scheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001623 if (!GetInputPaddingScheme<HalPolicy>(operation, 1, scheme, model, data) ||
1624 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1625 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1626 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1627 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1628 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001629 {
1630 return Fail("%s: Operation has invalid inputs", operationName);
1631 }
1632
Kevin May42477c12020-03-26 13:34:14 +00001633 if (Is12OrLaterOperand(*output))
arovir01b0717b52018-09-05 17:03:25 +01001634 {
Sadik Armagan15d63e22019-07-26 16:59:35 +01001635 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001636 }
FinnWilliamsArm493e9b72019-11-25 16:02:07 +00001637
1638 const armnnUtils::DataLayoutIndexed dataLayout(desc.m_DataLayout);
1639 const unsigned int inputWidth = inputInfo.GetShape()[dataLayout.GetWidthIndex()];
1640 const unsigned int inputHeight = inputInfo.GetShape()[dataLayout.GetHeightIndex()];
1641
1642 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, scheme);
1643 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, scheme);
arovir01b0717b52018-09-05 17:03:25 +01001644 }
1645
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001646 bool isSupported = false;
1647 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1648 IsPooling2dSupported,
1649 data.m_Backends,
1650 isSupported,
1651 inputInfo,
1652 outputInfo,
1653 desc);
1654 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001655 {
Éanna Ó Catháin3d1059c2018-10-11 15:53:04 +01001656 return false;
arovir01b0717b52018-09-05 17:03:25 +01001657 }
arovir01b0717b52018-09-05 17:03:25 +01001658
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001659 armnn::IConnectableLayer* pooling2dLayer = data.m_Network->AddPooling2dLayer(desc);
1660 if (!pooling2dLayer)
arovir01b0717b52018-09-05 17:03:25 +01001661 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001662 return Fail("%s: AddPooling2dLayer failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001663 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001664
1665 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, pooling2dLayer, data);
1666 if (!endLayer)
arovir01b0717b52018-09-05 17:03:25 +01001667 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001668 return Fail("%s: ProcessActivation failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001669 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001670
1671 input.Connect(pooling2dLayer->GetInputSlot(0));
1672
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001673 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001674}
1675
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001676template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001677 typename HalOperation = typename HalPolicy::Operation,
1678 typename HalModel = typename HalPolicy::Model>
1679bool ConvertAdd(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01001680{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001681 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01001682
1683 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1684 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1685
1686 if (!input0.IsValid() || !input1.IsValid())
1687 {
1688 return Fail("%s: Operation has invalid inputs", __func__);
1689 }
1690
1691 // The FuseActivation parameter is always the input index 2
1692 // and it should be optional
1693 ActivationFn activationFunction;
1694 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
1695 {
1696 return Fail("%s: Operation has invalid inputs", __func__);
1697 }
1698
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001699 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01001700 if (!outputOperand)
1701 {
1702 return false;
1703 }
1704
1705 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
1706 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
1707
1708 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
1709 if (IsDynamicTensor(outputInfo))
1710 {
1711 return Fail("%s: Dynamic output tensors are not supported", __func__);
1712 }
1713
1714 bool isSupported = false;
1715 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1716 IsAdditionSupported,
1717 data.m_Backends,
1718 isSupported,
1719 inputInfo0,
1720 inputInfo1,
1721 outputInfo);
1722 if (!isSupported)
1723 {
1724 return false;
1725 }
1726
1727 armnn::IConnectableLayer* const startLayer = data.m_Network->AddAdditionLayer();
1728 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
1729
1730 if (endLayer != nullptr)
1731 {
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001732 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001733 if (!isReshapeSupported)
1734 {
1735 return false;
1736 }
1737
Mike Kelly46272802019-08-14 17:00:48 +01001738 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1739 }
1740 else
1741 {
1742 return Fail("%s: ProcessActivation failed", __func__);
1743 }
1744}
1745
1746template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001747 typename HalOperation = typename HalPolicy::Operation,
1748 typename HalModel = typename HalPolicy::Model>
1749bool ConvertArgMinMax(const HalOperation& operation,
1750 const HalModel& model,
Francis Murtagh19fa0cc2019-11-19 12:06:47 +00001751 ConversionData& data,
1752 armnn::ArgMinMaxFunction argMinMaxFunction)
1753{
1754 ALOGV("argMinMaxFunction = %s", GetArgMinMaxFunctionAsCString(argMinMaxFunction));
1755
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001756 using HalOperand = typename HalPolicy::Operand;
Francis Murtagh19fa0cc2019-11-19 12:06:47 +00001757 using HalOperandType = typename HalPolicy::OperandType;
1758
1759 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1760
1761 if (!input0.IsValid())
1762 {
1763 return Fail("%s: Operation has invalid inputs", __func__);
1764 }
1765
1766 int32_t axis;
1767 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
1768 {
1769 return Fail("%s: Operation has invalid inputs. Failed to read axis.", __func__);
1770 }
1771
1772 const armnn::TensorInfo& inputInfo = input0.GetTensorInfo();
1773 int rank = static_cast<int>(inputInfo.GetNumDimensions());
1774
1775 if (((axis < -rank) && (axis < 0)) || ((axis >= rank) && (axis > 0)))
1776 {
1777 // Square bracket denotes inclusive n while parenthesis denotes exclusive n
1778 // E.g. Rank 4 tensor can have axis in range [-4, 3)
1779 // -1 == 3, -2 == 2, -3 == 1, -4 == 0
1780 return Fail("%s: Axis must be in range [-n, n)", __func__);
1781 }
1782
1783 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1784 if (!output)
1785 {
1786 return Fail("%s: Could not read output 0", __func__);
1787 }
1788
1789 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
1790
1791 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1792 if (IsDynamicTensor(outputInfo))
1793 {
1794 return Fail("%s: Dynamic output tensors are not supported", __func__);
1795 }
1796
1797 armnn::ArgMinMaxDescriptor descriptor;
1798 descriptor.m_Function = argMinMaxFunction;
1799 descriptor.m_Axis = axis;
1800
1801 bool isSupported = false;
1802 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1803 IsArgMinMaxSupported,
1804 data.m_Backends,
1805 isSupported,
1806 inputInfo0,
1807 outputInfo,
1808 descriptor);
1809 if (!isSupported)
1810 {
1811 return false;
1812 }
1813
1814 armnn::IConnectableLayer* layer = data.m_Network->AddArgMinMaxLayer(descriptor);
1815 assert(layer != nullptr);
1816
1817 input0.Connect(layer->GetInputSlot(0));
1818
1819 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1820}
1821
1822template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001823 typename HalOperation = typename HalPolicy::Operation,
1824 typename HalModel = typename HalPolicy::Model>
1825bool ConvertConcatenation(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kellyb8805202019-07-31 17:25:43 +01001826{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001827 using HalOperand = typename HalPolicy::Operand;
Mike Kellyb8805202019-07-31 17:25:43 +01001828 using HalOperandType = typename HalPolicy::OperandType;
1829
1830 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
1831 if (operation.inputs.size() <= 1)
1832 {
1833 return Fail("%s: Operation has insufficient arguments", __func__);
1834 }
1835
1836 // Get inputs and outputs
1837 const std::size_t numInputTensors = operation.inputs.size() - 1;
1838
1839 int32_t concatDim;
1840 if (!GetInputScalar<HalPolicy>(operation, numInputTensors, HalOperandType::INT32, concatDim, model, data))
1841 {
1842 return Fail("%s: Operation has invalid inputs", __func__);
1843 }
1844
1845 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1846 if (!outputOperand)
1847 {
1848 return Fail("%s: Operation has no outputs", __func__);
1849 }
1850
1851
1852 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
1853 armnn::TensorShape outputShape = outputInfo.GetShape();
1854
1855 //
1856 // handle negative concat dims along the lines of tensorflow as described here:
1857 // https://www.tensorflow.org/api_docs/python/tf/concat
1858 // "negative axis refers to axis + rank(values)-th dimension"
1859 //
1860 if (concatDim < 0)
1861 {
1862 concatDim += outputShape.GetNumDimensions();
1863 }
1864
1865 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
1866 {
1867 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
1868 }
1869
1870 std::vector<LayerInputHandle> inputHandles;
1871 std::vector<armnn::TensorShape> inputShapes;
1872
1873 inputHandles.reserve(numInputTensors);
1874 inputShapes.reserve(numInputTensors);
1875
1876 bool inputsHaveBeenReshaped = false;
1877 unsigned int tensorDimensionsAdded = 0;
1878
1879 for (uint32_t i = 0; i < numInputTensors; ++i)
1880 {
1881 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, i, model);
1882 if (!operand)
1883 {
1884 return Fail("%s: Operation has invalid inputs", __func__);
1885 }
1886
Teresa Charlin3b959602019-10-31 17:05:47 +00001887 LayerInputHandle operandInputHandle = ConvertToLayerInputHandle<HalPolicy>(operation, i, model, data);
1888 if (!operandInputHandle.IsValid())
1889 {
1890 return Fail("%s: Operation has invalid inputs", __func__);
1891 }
Mike Kellyb8805202019-07-31 17:25:43 +01001892
Teresa Charlin3b959602019-10-31 17:05:47 +00001893 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
Mike Kellyb8805202019-07-31 17:25:43 +01001894 if (operandShape.GetNumDimensions() == 0)
1895 {
1896 return Fail("%s: Operands with rank 0 are not supported", __func__);
1897 }
1898
1899 if (RequiresReshape(operandShape))
1900 {
1901 inputsHaveBeenReshaped = true;
1902
1903 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
1904
1905 // Expand the tensor to three dimensions
1906 if (operandShape.GetNumDimensions() == 2)
1907 {
1908 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
1909 tensorDimensionsAdded = 1;
1910 }
1911 else
1912 {
1913 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
1914 tensorDimensionsAdded = 2;
1915 }
1916
Kevin Mayaed08ac2019-12-12 16:33:31 +00001917 armnn::ReshapeDescriptor reshapeDescriptor;
1918 reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
1919
1920 bool isSupported = false;
1921 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1922 IsReshapeSupported,
1923 data.m_Backends,
1924 isSupported,
1925 operandInputHandle.GetTensorInfo(),
1926 reshapeInfo,
1927 reshapeDescriptor);
1928 if (!isSupported)
1929 {
1930 return false;
1931 }
1932
Mike Kellyb8805202019-07-31 17:25:43 +01001933 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
1934 *data.m_Network,
1935 operandInputHandle,
1936 reshapeInfo
1937 );
1938
1939 // Point to the reshape operation rather then the input operation
1940 operandShape = reshapeInfo.GetShape();
1941 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
1942 }
1943
1944 inputShapes.emplace_back(operandShape);
1945 inputHandles.emplace_back(operandInputHandle);
1946
1947 if (!inputHandles.back().IsValid())
1948 {
1949 return Fail("%s: Operation has invalid inputs", __func__);
1950 }
1951 }
1952
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +01001953 ARMNN_ASSERT(inputShapes.size() == inputHandles.size());
Mike Kellyb8805202019-07-31 17:25:43 +01001954
1955 if (inputsHaveBeenReshaped)
1956 {
1957 // Adjust the concatenation dimension by the amount of dimensions added (if any)
1958 concatDim += tensorDimensionsAdded;
1959
1960 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
1961 if (tensorDimensionsAdded == 1)
1962 {
1963 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
1964 }
1965 else if (tensorDimensionsAdded == 2)
1966 {
1967 outputShape = armnn::TensorShape({1, 1, outputShape[0]});
1968 }
1969 }
1970
1971 // Check if permutations is required and get the pair of permutations required for the concatenation.
1972 // Permutation is required when the concat dimension is 2 for a 4D tensor or 1 for a 3D tensor.
1973 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
1974 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
1975
1976 bool needPermute =
1977 CreateConcatPermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
1978
1979 if (needPermute)
1980 {
Mike Kelly4a956582020-02-28 10:32:09 +00001981 outputShape = armnnUtils::TransposeTensorShape(outputShape, permutationPair.first);
Mike Kellyb8805202019-07-31 17:25:43 +01001982 }
1983
1984 outputInfo.SetShape(outputShape);
1985
1986 // this is no-op for identity swizzles, otherwise it replaces both
1987 // the handles and shapes with the swizzled layer output handles and shapes
Kevin Mayaed08ac2019-12-12 16:33:31 +00001988 if (!CheckReshapeSupported(data, inputHandles, inputShapes, permutationPair.first, outputInfo))
1989 {
1990 return false;
1991 }
Mike Kellyb8805202019-07-31 17:25:43 +01001992
1993 // Create an armnn concat layer descriptor - this will also perform validation on the input shapes
1994 armnn::OriginsDescriptor concatDescriptor;
1995
1996 try
1997 {
1998 // The concat descriptor is always created across the only supported concat dimension
1999 // which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
2000 concatDescriptor =
2001 armnn::CreateDescriptorForConcatenation(inputShapes.begin(), inputShapes.end(), concatDim);
2002 }
Derek Lambertib9cb8442019-11-28 13:34:48 +00002003 catch (std::exception& error)
Mike Kellyb8805202019-07-31 17:25:43 +01002004 {
2005 return Fail("%s: Error preparing concat descriptor. %s", __func__, error.what());
2006 }
2007
2008 // Validate the output shape is correct given the input shapes based on the
2009 // only valid concat dimension which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
2010 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
2011 {
2012 return Fail("%s: Error validating the output shape for concat", __func__);
2013 }
2014
2015 std::vector<const armnn::TensorInfo*> inputTensorInfos;
2016 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
2017 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
2018
2019 bool isSupported = false;
2020 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2021 IsConcatSupported,
2022 data.m_Backends,
2023 isSupported,
2024 inputTensorInfos,
2025 outputInfo,
2026 concatDescriptor);
2027 if (!isSupported)
2028 {
2029 return false;
2030 }
2031
2032 armnn::IConnectableLayer* layer = data.m_Network->AddConcatLayer(concatDescriptor);
2033 assert(layer != nullptr);
2034 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
2035
2036 // Connect inputs to the layer
2037 const int numInputSlots = layer->GetNumInputSlots();
2038 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
2039 for (int i = 0; i < numInputSlots; ++i)
2040 {
2041 // connect the input directly to the merge (concat) layer
2042 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
2043 }
2044
2045 if (needPermute)
2046 {
Mike Kelly4a956582020-02-28 10:32:09 +00002047 armnn::TransposeDescriptor transposeDesc;
2048 transposeDesc.m_DimMappings = permutationPair.second;
Kevin Mayaed08ac2019-12-12 16:33:31 +00002049
2050 bool isSupported = false;
2051 FORWARD_LAYER_SUPPORT_FUNC(__func__,
Mike Kelly4a956582020-02-28 10:32:09 +00002052 IsTransposeSupported,
Kevin Mayaed08ac2019-12-12 16:33:31 +00002053 data.m_Backends,
2054 isSupported,
2055 layer->GetOutputSlot(0).GetTensorInfo(),
2056 outputInfo,
Mike Kelly4a956582020-02-28 10:32:09 +00002057 transposeDesc);
Kevin Mayaed08ac2019-12-12 16:33:31 +00002058 if (!isSupported)
2059 {
2060 return false;
2061 }
Mike Kellyb8805202019-07-31 17:25:43 +01002062 // Add permutation layer and connect the output to it, the permutation becomes the output layer
Mike Kelly4a956582020-02-28 10:32:09 +00002063 armnn::IConnectableLayer& deswizzleLayer = AddTransposeLayer(*data.m_Network,
2064 layer->GetOutputSlot(0),
2065 permutationPair.second);
Mike Kellyb8805202019-07-31 17:25:43 +01002066 layer = &deswizzleLayer;
2067 }
2068
2069 if (inputsHaveBeenReshaped)
2070 {
2071 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
2072
2073 // Undo the reshape knowing the amount of dimensions added
2074 if (tensorDimensionsAdded == 1)
2075 {
2076 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
2077 afterConcatInfo.GetShape()[2] }));
2078 }
2079 else if (tensorDimensionsAdded == 2)
2080 {
2081 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2] }));
2082 }
2083
Kevin Mayaed08ac2019-12-12 16:33:31 +00002084 armnn::ReshapeDescriptor reshapeDescriptor;
2085 reshapeDescriptor.m_TargetShape = afterConcatInfo.GetShape();
2086
2087 bool isSupported = false;
2088 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2089 IsReshapeSupported,
2090 data.m_Backends,
2091 isSupported,
2092 layer->GetOutputSlot(0).GetTensorInfo(),
2093 afterConcatInfo,
2094 reshapeDescriptor);
2095 if (!isSupported)
2096 {
2097 return false;
2098 }
2099
Mike Kellyb8805202019-07-31 17:25:43 +01002100 layer = &AddReshapeLayer(
2101 *data.m_Network,
2102 layer->GetOutputSlot(0),
2103 afterConcatInfo
2104 );
2105 }
2106
2107 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2108}
2109
2110template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002111 typename HalOperation = typename HalPolicy::Operation,
2112 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01002113bool ConvertConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2114{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002115 using HalOperand = typename HalPolicy::Operand;
2116 using HalOperandType = typename HalPolicy::OperandType;
2117
2118 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002119 if (!input.IsValid())
2120 {
2121 return Fail("%s: Operation has invalid inputs", __func__);
2122 }
2123
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002124 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002125 if (!output)
2126 {
2127 return Fail("%s: Could not read output 0", __func__);
2128 }
2129
2130 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002131 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002132
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002133 if (IsDynamicTensor(outputInfo))
2134 {
2135 return Fail("%s: Dynamic output tensors are not supported", __func__);
2136 }
2137
Mike Kellyb5fdf382019-06-11 16:35:25 +01002138 // ArmNN does not currently support non-fixed weights or bias
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002139 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2140 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002141
2142 if (!weightsPin.IsValid() || !biasPin.IsValid())
2143 {
2144 return Fail("%s: Operation has invalid inputs", __func__);
2145 }
2146
2147 armnn::ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002148 armnn::ConstTensor bias = biasPin.GetConstTensor();
Mike Kellyb5fdf382019-06-11 16:35:25 +01002149 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2150
2151 armnn::Convolution2dDescriptor desc;
2152 desc.m_DataLayout = armnn::DataLayout::NHWC;
2153 ActivationFn activation;
2154
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002155 if (operation.inputs.size() == 10)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002156 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002157 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2158 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2159 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2160 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2161 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2162 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002163 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002164 {
2165 return Fail("%s: Operation has invalid inputs", __func__);
2166 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01002167 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002168 else if (operation.inputs.size() == 7)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002169 {
2170 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002171 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
2172 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2173 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002174 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002175 {
2176 return Fail("%s: Operation has invalid inputs", __func__);
2177 }
2178
2179 const uint32_t kernelX = weights.GetShape()[2];
2180 const uint32_t kernelY = weights.GetShape()[1];
2181 const uint32_t inputX = inputInfo.GetShape()[2];
2182 const uint32_t inputY = inputInfo.GetShape()[1];
2183
2184 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
2185 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002186 }
2187 else
2188 {
2189 return Fail("%s: Unsupported number of operation inputs", __func__);
2190 }
2191
2192 desc.m_BiasEnabled = true;
2193 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
2194
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002195 bool isSupported = false;
2196 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2197 IsConvolution2dSupported,
2198 data.m_Backends,
2199 isSupported,
2200 inputInfo,
2201 outputInfo,
2202 desc,
2203 weights.GetInfo(),
2204 biases);
2205 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002206 {
2207 return false;
2208 }
2209
2210 armnn::IConnectableLayer* startLayer =
2211 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2212
2213 if (!startLayer)
2214 {
2215 return Fail("%s: AddConvolution2dLayer failed", __func__);
2216 }
2217
2218 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2219
2220 if (!endLayer)
2221 {
2222 return Fail("%s: ProcessActivation failed", __func__);
2223 }
2224
2225 input.Connect(startLayer->GetInputSlot(0));
2226
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002227 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002228}
2229
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002230template<typename HalPolicy,
2231 typename HalOperation = typename HalPolicy::Operation,
2232 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +01002233bool ConvertDepthToSpace(const HalOperation& operation, const HalModel& model, ConversionData& data)
2234{
2235 using HalOperand = typename HalPolicy::Operand;
2236 using HalOperandType = typename HalPolicy::OperandType;
2237
2238 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2239 if (!input.IsValid() )
2240 {
2241 return Fail("%s: Operation has invalid inputs", __func__);
2242 }
2243
2244 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2245 unsigned int rank = inputInfo.GetNumDimensions();
2246 if (rank != 4)
2247 {
2248 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2249 }
2250
2251 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2252 if (!output)
2253 {
2254 return Fail("%s: Could not read output 0", __func__);
2255 }
2256
2257 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2258 if (IsDynamicTensor(outputInfo))
2259 {
2260 return Fail("%s: Dynamic output tensors are not supported", __func__);
2261 }
2262
2263 armnn::DepthToSpaceDescriptor descriptor;
2264
2265 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, descriptor.m_BlockSize, model, data);
2266 if (descriptor.m_BlockSize <= 1)
2267 {
2268 return Fail("%s: Block size must be at least 1 in all dimensions");
2269 }
2270
2271 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
Kevin May42477c12020-03-26 13:34:14 +00002272 if (Is12OrLaterOperand(*output))
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +01002273 {
2274 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2275 }
2276
2277 bool isSupported = false;
2278 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2279 IsDepthToSpaceSupported,
2280 data.m_Backends,
2281 isSupported,
2282 inputInfo,
2283 outputInfo,
2284 descriptor);
2285 if (!isSupported)
2286 {
2287 return false;
2288 }
2289
2290 armnn::IConnectableLayer* const layer = data.m_Network->AddDepthToSpaceLayer(descriptor);
2291 assert(layer != nullptr);
2292 input.Connect(layer->GetInputSlot(0));
2293
2294 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2295}
2296
2297template<typename HalPolicy,
2298 typename HalOperation = typename HalPolicy::Operation,
2299 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01002300bool ConvertDepthwiseConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2301{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002302 using HalOperand = typename HalPolicy::Operand;
2303 using HalOperandType = typename HalPolicy::OperandType;
2304
2305 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002306
2307 if (!input.IsValid())
2308 {
2309 return Fail("%s: Operation has invalid inputs", __func__);
2310 }
2311
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002312 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002313
2314 if (!output)
2315 {
2316 return Fail("%s: Could not read output 0", __func__);
2317 }
2318
2319 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002320 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002321
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002322 if (IsDynamicTensor(outputInfo))
2323 {
2324 return Fail("%s: Dynamic output tensors are not supported", __func__);
2325 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01002326
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002327 // ArmNN does not currently support non-fixed weights or bias
Mike Kellyb5fdf382019-06-11 16:35:25 +01002328 // 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 +01002329 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002330
2331 if (weightsOperand == nullptr)
2332 {
2333 return Fail("%s: Operand is invalid", __func__);
2334 }
2335 armnn::DepthwiseConvolution2dDescriptor desc;
2336 desc.m_DataLayout = armnn::DataLayout::NHWC;
2337
Mike Kellyb5fdf382019-06-11 16:35:25 +01002338 // Reinterpret weight data as [ H, W, I, M ]
2339 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
2340 weightsOperand->dimensions[2],
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002341 inputInfo.GetShape()[3],
2342 weightsOperand->dimensions[3] / inputInfo.GetShape()[3] });
Mike Kellyb5fdf382019-06-11 16:35:25 +01002343
2344 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
2345 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
2346
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002347 const ConstTensorPin weightsPin =
2348 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2349 1,
2350 model,
2351 data,
2352 HWIMToMIHW,
2353 &weightsShape);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002354
2355 // Bias is a 1D tensor
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002356 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002357
2358 if (!weightsPin.IsValid() || !biasPin.IsValid())
2359 {
2360 return Fail("%s: Operation has invalid inputs", __func__);
2361 }
2362
2363 armnn::ConstTensor weights = weightsPin.GetConstTensor();
2364 armnn::ConstTensor bias = biasPin.GetConstTensor();
2365 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2366
2367 ActivationFn activation;
2368
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002369 if (operation.inputs.size() == 11)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002370 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002371 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2372 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2373 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2374 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2375 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2376 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002377 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002378 {
2379 return Fail("%s: Operation has invalid inputs", __func__);
2380 }
2381 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002382 else if (operation.inputs.size() == 8)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002383 {
2384 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002385 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
2386 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2387 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002388 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002389 {
2390 return Fail("%s: Operation has invalid inputs", __func__);
2391 }
2392
2393 const uint32_t kernelX = weights.GetShape()[3];
2394 const uint32_t kernelY = weights.GetShape()[2];
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002395 const uint32_t inputX = inputInfo.GetShape()[2];
2396 const uint32_t inputY = inputInfo.GetShape()[1];
Mike Kellyb5fdf382019-06-11 16:35:25 +01002397
2398 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
2399 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
2400 }
2401 else
2402 {
2403 return Fail("%s: Unsupported number of operation inputs", __func__);
2404 }
2405
2406 desc.m_BiasEnabled = true;
2407 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
2408
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002409 bool isSupported = false;
2410 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2411 IsDepthwiseConvolutionSupported,
2412 data.m_Backends,
2413 isSupported,
2414 inputInfo,
2415 outputInfo,
2416 desc,
2417 weights.GetInfo(),
2418 biases);
2419 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002420 {
2421 return false;
2422 }
2423
2424 armnn::IConnectableLayer* startLayer =
2425 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2426 if (!startLayer)
2427 {
2428 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
2429 }
2430
2431 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2432 if (!endLayer)
2433 {
2434 return Fail("%s: ProcessActivation failed", __func__);
2435 }
2436
2437 input.Connect(startLayer->GetInputSlot(0));
2438
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002439 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01002440}
2441
Mike Kelly3c673942019-07-25 09:26:06 +01002442template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002443 typename HalOperation = typename HalPolicy::Operation,
2444 typename HalModel = typename HalPolicy::Model>
2445bool ConvertDequantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly3c673942019-07-25 09:26:06 +01002446{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002447 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002448
2449 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2450 if (!input.IsValid())
2451 {
2452 return Fail("%s: Operation has invalid input", __func__);
2453 }
2454
Sadik Armagan98c0f662019-11-21 15:54:36 +00002455 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2456 const armnn::Optional<unsigned int>& quantizationDim = inputInfo.GetQuantizationDim();
2457 if (quantizationDim.has_value() && quantizationDim.value() != 0)
2458 {
2459 return Fail("%s: Operation has quantization dimension different than 0", __func__);
2460 }
2461
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002462 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002463 if (!outputOperand)
2464 {
2465 return Fail("%s: Operation has invalid outputs", __func__);
2466 }
2467
2468 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2469 if (IsDynamicTensor(outputInfo))
2470 {
2471 return Fail("%s: Dynamic output tensors are not supported", __func__);
2472 }
2473
2474 bool isSupported = false;
2475 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2476 IsDequantizeSupported,
2477 data.m_Backends,
2478 isSupported,
Sadik Armagan98c0f662019-11-21 15:54:36 +00002479 inputInfo,
2480 outputInfo);
Mike Kelly46272802019-08-14 17:00:48 +01002481 if (!isSupported)
2482 {
2483 return false;
2484 }
2485
2486 armnn::IConnectableLayer* const layer = data.m_Network->AddDequantizeLayer();
2487 assert(layer != nullptr);
2488 input.Connect(layer->GetInputSlot(0));
2489
2490 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2491}
2492
2493template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002494 typename HalOperation = typename HalPolicy::Operation,
2495 typename HalModel = typename HalPolicy::Model>
2496bool ConvertDiv(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002497{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002498 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002499
2500 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2501 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
2502
2503 if (!input0.IsValid() || !input1.IsValid())
2504 {
2505 return Fail("%s: Operation has invalid inputs", __func__);
2506 }
2507
2508 // The FuseActivation parameter is always the input index 2
2509 // and it should be optional
2510 ActivationFn activationFunction;
2511 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2512 {
2513 return Fail("%s: Operation has invalid inputs", __func__);
2514 }
2515
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002516 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002517 if (!output)
2518 {
2519 return Fail("%s: Could not read output 0", __func__);
2520 }
2521
2522 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2523 if (IsDynamicTensor(outputInfo))
2524 {
2525 return Fail("%s: Dynamic output tensors are not supported", __func__);
2526 }
2527
2528 bool isSupported = false;
2529 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2530 IsDivisionSupported,
2531 data.m_Backends,
2532 isSupported,
2533 input0.GetTensorInfo(),
2534 input1.GetTensorInfo(),
2535 outputInfo);
2536 if (!isSupported)
2537 {
2538 return false;
2539 }
2540
2541 armnn::IConnectableLayer* const startLayer = data.m_Network->AddDivisionLayer();
2542 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2543
2544 if (endLayer)
2545 {
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00002546 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01002547 if (!isReshapeSupported)
2548 {
2549 return false;
2550 }
2551
Mike Kelly46272802019-08-14 17:00:48 +01002552 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2553 }
2554 return Fail("%s: ProcessActivation failed", __func__);
2555}
2556
2557template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002558 typename HalOperation = typename HalPolicy::Operation,
2559 typename HalModel = typename HalPolicy::Model>
2560bool ConvertFloor(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002561{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002562 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002563
2564 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2565 if (!input.IsValid())
2566 {
2567 return Fail("%s: Operation has invalid inputs", __func__);
2568 }
2569
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002570 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002571 if (!outputOperand)
2572 {
2573 return Fail("%s: Operation has invalid outputs", __func__);
2574 }
2575
2576 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2577 if (IsDynamicTensor(outputInfo))
2578 {
2579 return Fail("%s: Dynamic output tensors are not supported", __func__);
2580 }
2581
2582 bool isSupported = false;
2583 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2584 IsFloorSupported,
2585 data.m_Backends,
2586 isSupported,
2587 input.GetTensorInfo(),
2588 outputInfo);
2589 if (!isSupported)
2590 {
2591 return false;
2592 }
2593
2594 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
2595 assert(layer != nullptr);
2596 input.Connect(layer->GetInputSlot(0));
2597
2598 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2599}
2600
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002601inline bool IsQSymm8(const V1_0::Operand&)
2602{
2603 return false;
2604}
2605
Kevin May42477c12020-03-26 13:34:14 +00002606#if defined(ARMNN_ANDROID_NN_V1_2) || defined(ARMNN_ANDROID_NN_V1_3)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002607
2608inline bool IsQSymm8(const V1_2::Operand& operand)
2609{
2610 return operand.type == V1_2::OperandType::TENSOR_QUANT8_SYMM;
2611}
2612
2613#endif
2614
Kevin May42477c12020-03-26 13:34:14 +00002615#ifdef ARMNN_ANDROID_NN_V1_3
2616
2617inline bool IsQSymm8(const V1_3::Operand& operand)
2618{
2619 return operand.type == V1_3::OperandType::TENSOR_QUANT8_SYMM;
2620}
2621
2622#endif
2623
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002624enum class DequantizeStatus
2625{
2626 SUCCESS,
2627 NOT_REQUIRED,
2628 INVALID_OPERAND
2629};
2630
2631using DequantizeResult = std::tuple<std::unique_ptr<float[]>, size_t, armnn::TensorInfo, DequantizeStatus>;
2632
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002633template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002634 typename HalOperation = typename HalPolicy::Operation,
2635 typename HalModel = typename HalPolicy::Model>
2636DequantizeResult DequantizeIfRequired(size_t operand_index,
2637 const HalOperation& operation,
2638 const HalModel& model,
2639 const ConversionData& data)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002640{
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002641 using HalOperand = typename HalPolicy::Operand;
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002642
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002643 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, operand_index, model);
Sadik Armagand0811942019-11-18 17:11:21 +00002644 if (!weightsOperand)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002645 {
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002646 return { nullptr, 0, armnn::TensorInfo(), DequantizeStatus::INVALID_OPERAND };
Sadik Armagand0811942019-11-18 17:11:21 +00002647 }
2648
2649 if (IsOperandConstant<HalPolicy>(*weightsOperand))
2650 {
2651 // Weights are already constant
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002652 return { nullptr, 0, armnn::TensorInfo(), DequantizeStatus::NOT_REQUIRED };
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002653 }
2654
2655 const size_t weightsInputIndex = operation.inputs[operand_index];
2656
2657 // The weights are a non const tensor, this indicates they might be the output of a dequantize op.
2658 // Iterate over the nodes and find the previous operation which should be DEQUANTIZE
Kevin May42477c12020-03-26 13:34:14 +00002659 for (uint32_t operationIdx = 0; operationIdx < getMainModel(model).operations.size(); ++operationIdx)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002660 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002661 // Search for the DEQUANTIZE op which has the operand with index equal to operandIndex
Kevin May42477c12020-03-26 13:34:14 +00002662 const auto& operationIt = getMainModel(model).operations[operationIdx];
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002663 if (operationIt.type != HalPolicy::OperationType::DEQUANTIZE)
2664 {
2665 continue;
2666 }
2667
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002668 size_t outOpIndex = weightsInputIndex + 1;
2669 for (size_t i = 0; outOpIndex != weightsInputIndex && i < operationIt.outputs.size(); ++i)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002670 {
2671 outOpIndex = operationIt.outputs[i];
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002672 }
2673
2674 if (outOpIndex != weightsInputIndex)
2675 {
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002676 continue;
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002677 }
2678
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002679 const HalOperand* operand = GetInputOperand<HalPolicy>(operationIt, 0, model);
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +01002680 ARMNN_ASSERT(operand);
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002681
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002682 if (!IsQSymm8(*operand))
2683 {
2684 // Only supporting dequantize from QSYMM8 to FLOAT
2685 break;
2686 }
2687
2688 // Allocate a new buffer for the dequantized data and manually dequantize
2689 const void* startValue = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
2690 if (!startValue)
2691 {
2692 // Failed to get the operand address
2693 break;
2694 }
2695
2696 const uint8_t* quantizedBuffer = reinterpret_cast<const uint8_t*>(startValue);
2697 size_t dequantizedBufferLength = operand->location.length;
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002698 const float quantizationScale = operand->scale;
2699
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002700 auto dequantizedBuffer = std::make_unique<float[]>(dequantizedBufferLength + 1);
2701 for (size_t i = 0; i < dequantizedBufferLength; ++i)
2702 {
2703 float* dstPtr = dequantizedBuffer.get();
Narumol Prangnawarat4d07e5e2020-04-06 16:46:21 +01002704 ARMNN_ASSERT(dstPtr);
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002705 *dstPtr++ = quantizedBuffer[i] * quantizationScale;
2706 }
2707
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002708 // Construct tensor info for dequantized ConstTensor
2709 armnn::TensorInfo tensorInfo(operand->dimensions.size(),
2710 operand->dimensions.data(),
2711 armnn::DataType::Float32);
2712
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002713 return { std::move(dequantizedBuffer), dequantizedBufferLength * sizeof(float),
2714 std::move(tensorInfo),
2715 DequantizeStatus::SUCCESS };
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002716 }
2717
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002718 return { nullptr, 0, armnn::TensorInfo() , DequantizeStatus::NOT_REQUIRED};
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002719}
2720
2721template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002722 typename HalOperation = typename HalPolicy::Operation,
2723 typename HalModel = typename HalPolicy::Model>
2724ConstTensorPin DequantizeAndMakeConstTensorPin(const HalOperation& operation,
2725 const HalModel& model,
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002726 const ConversionData& data,
2727 size_t operandIndex,
2728 bool optional = false)
2729{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002730 DequantizeResult dequantized = DequantizeIfRequired<HalPolicy>(operandIndex,operation, model, data);
2731
2732 DequantizeStatus status = std::get<3>(dequantized);
2733 switch (status)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002734 {
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002735 case DequantizeStatus::INVALID_OPERAND:
2736 {
2737 // return invalid const tensor pin
2738 return ConstTensorPin();
2739 }
2740 case DequantizeStatus::NOT_REQUIRED:
2741 {
2742 return ConvertOperationInputToConstTensorPin<HalPolicy>(
2743 operation, operandIndex, model, data, g_DontPermute, nullptr, optional);
2744 }
2745 case DequantizeStatus::SUCCESS:
2746 default:
2747 {
2748 return ConstTensorPin(
2749 std::get<2>(dequantized), std::get<0>(dequantized).get(), std::get<1>(dequantized), g_DontPermute);
2750 }
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002751 }
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002752}
2753
2754
Mike Kelly46272802019-08-14 17:00:48 +01002755template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002756 typename HalOperation = typename HalPolicy::Operation,
2757 typename HalModel = typename HalPolicy::Model>
2758bool ConvertFullyConnected(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002759{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002760 using HalOperand = typename HalPolicy::Operand;
2761
Mike Kelly46272802019-08-14 17:00:48 +01002762 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2763 if (!input.IsValid())
2764 {
2765 return Fail("%s: Operation has invalid inputs", __func__);
2766 }
2767
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002768 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002769 if (!output)
2770 {
2771 return Fail("%s: Could not read output 0", __func__);
2772 }
2773
2774 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2775 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2776
2777 if (IsDynamicTensor(outputInfo))
2778 {
2779 return Fail("%s: Dynamic output tensors are not supported", __func__);
2780 }
2781
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002782 ConstTensorPin weightsPin = DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1);
2783 ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data); // 1D
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002784
2785 if (!weightsPin.IsValid())
Mike Kelly46272802019-08-14 17:00:48 +01002786 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002787 return Fail("%s: Operation has invalid weights", __func__);
2788 }
2789
2790 if (!biasPin.IsValid())
2791 {
2792 return Fail("%s: Operation has invalid bias", __func__);
Mike Kelly46272802019-08-14 17:00:48 +01002793 }
2794
2795 armnn::ConstTensor weights = weightsPin.GetConstTensor();
2796 armnn::ConstTensor bias = biasPin.GetConstTensor();
2797 armnn::TensorInfo reshapedInfo = inputInfo;
2798
2799 try
2800 {
2801 reshapedInfo.SetShape(FlattenFullyConnectedInput(inputInfo.GetShape(), weights.GetInfo().GetShape()));
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002802 }
2803 catch (const std::exception& e)
2804 {
Mike Kelly46272802019-08-14 17:00:48 +01002805 return Fail("%s: %s", __func__, e.what());
2806 }
2807
2808 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
2809 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
2810
2811 ActivationFn activationFunction;
2812 if (!GetInputActivationFunction<HalPolicy>(operation, 3, activationFunction, model, data))
2813 {
2814 return Fail("%s: Operation has invalid inputs", __func__);
2815 }
2816
2817 armnn::FullyConnectedDescriptor desc;
2818 desc.m_TransposeWeightMatrix = true;
2819 desc.m_BiasEnabled = true;
2820
FinnWilliamsArm7b8d2e62020-01-08 14:57:47 +00002821 if (!VerifyFullyConnectedShapes(reshapedInfo.GetShape(),
2822 weights.GetInfo().GetShape(),
2823 outputInfo.GetShape(),
2824 desc.m_TransposeWeightMatrix))
2825 {
2826 return Fail("%s: Expected outputShape does not match actual outputShape", __func__);
2827 }
2828
Mike Kelly46272802019-08-14 17:00:48 +01002829 bool isSupported = false;
2830 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2831 IsFullyConnectedSupported,
2832 data.m_Backends,
2833 isSupported,
2834 reshapedInfo,
2835 outputInfo,
2836 weights.GetInfo(),
2837 bias.GetInfo(),
2838 desc);
2839 if (!isSupported)
2840 {
2841 return false;
2842 }
2843
2844 armnn::IConnectableLayer* startLayer =
2845 data.m_Network->AddFullyConnectedLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2846 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2847
2848 if (endLayer != nullptr)
2849 {
2850 if (inputInfo.GetNumDimensions() > 2U)
2851 {
2852 armnn::ReshapeDescriptor reshapeDescriptor;
2853 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
2854
2855 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
2856 assert(reshapeLayer != nullptr);
2857 input.Connect(reshapeLayer->GetInputSlot(0));
2858 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
2859 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
2860 }
2861 else
2862 {
2863 input.Connect(startLayer->GetInputSlot(0));
2864 }
2865
2866 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2867 }
2868 else
2869 {
2870 return Fail("%s: ProcessActivation failed", __func__);
2871 }
2872}
2873
2874template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002875 typename HalOperation = typename HalPolicy::Operation,
2876 typename HalModel = typename HalPolicy::Model>
2877bool ConvertL2Normalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002878{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002879 using HalOperand = typename HalPolicy::Operand;
2880
Mike Kelly999e2092019-08-15 10:46:46 +01002881 if (operation.inputs.size() != 1)
2882 {
2883 return Fail("%s: Optional inputs are not supported", __func__);
2884 }
2885
Mike Kelly46272802019-08-14 17:00:48 +01002886 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2887 if (!input.IsValid())
2888 {
2889 return Fail("%s: Operation has invalid inputs", __func__);
2890 }
2891
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002892 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002893 if (!output)
2894 {
2895 return Fail("%s: Could not read output 0", __func__);
2896 }
2897
2898 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2899 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2900
2901 if (IsDynamicTensor(outputInfo))
2902 {
2903 return Fail("%s: Dynamic output tensors are not supported", __func__);
2904 }
2905 if (outputInfo.GetNumDimensions() != 4u)
2906 {
2907 return Fail("%s: Tensor Rank other than 4 is not supported", __func__);
2908 }
2909
2910 armnn::L2NormalizationDescriptor desc;
2911 desc.m_DataLayout = armnn::DataLayout::NHWC;
2912
2913 bool isSupported = false;
2914 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2915 IsL2NormalizationSupported,
2916 data.m_Backends,
2917 isSupported,
2918 inputInfo,
2919 outputInfo,
2920 desc);
2921 if (!isSupported)
2922 {
2923 return false;
2924 }
2925
2926 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer(desc);
2927 assert(layer != nullptr);
2928 input.Connect(layer->GetInputSlot(0));
2929
2930 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2931}
2932
2933template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002934 typename HalOperation = typename HalPolicy::Operation,
2935 typename HalModel = typename HalPolicy::Model>
2936bool ConvertLocalResponseNormalization(const HalOperation& operation,
2937 const HalModel& model,
Mike Kelly46272802019-08-14 17:00:48 +01002938 ConversionData& data)
2939{
Mike Kelly999e2092019-08-15 10:46:46 +01002940 if (operation.inputs.size() != 5)
2941 {
2942 return Fail("%s: Optional inputs are not supported", __func__);
2943 }
2944
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002945 using HalOperand = typename HalPolicy::Operand;
2946 using HalOperandType = typename HalPolicy::OperandType;
Mike Kelly46272802019-08-14 17:00:48 +01002947
2948 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2949 if (!input.IsValid())
2950 {
2951 return Fail("%s: Operation has invalid inputs", __func__);
2952 }
2953
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002954 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002955 if (!output)
2956 {
2957 return Fail("%s: Could not read output 0", __func__);
2958 }
2959
2960 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2961 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2962
2963 if (IsDynamicTensor(outputInfo))
2964 {
2965 return Fail("%s: Dynamic output tensors are not supported", __func__);
2966 }
2967 if (outputInfo.GetNumDimensions() != 4u)
2968 {
2969 return Fail("%s: Tensor Rank other than 4 is not supported", __func__);
2970 }
2971
2972 armnn::NormalizationDescriptor descriptor;
2973 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
2974 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
2975 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
2976
2977 if (!input.IsValid() ||
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002978 !GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, descriptor.m_NormSize, model, data) ||
Mike Kelly46272802019-08-14 17:00:48 +01002979 !GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_K, model, data) ||
2980 !GetInputFloat32<HalPolicy>(operation, 3, descriptor.m_Alpha, model, data) ||
2981 !GetInputFloat32<HalPolicy>(operation, 4, descriptor.m_Beta, model, data))
2982 {
2983 return Fail("%s: Operation has invalid inputs", __func__);
2984 }
2985
2986 // ArmNN expects normSize to be the full size of the normalization
2987 // window rather than the radius as in AndroidNN.
2988 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
2989
2990 bool isSupported = false;
2991 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2992 IsNormalizationSupported,
2993 data.m_Backends,
2994 isSupported,
2995 inputInfo,
2996 outputInfo,
2997 descriptor);
2998 if (!isSupported)
2999 {
3000 return false;
3001 }
3002
3003
3004 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
3005 assert(layer != nullptr);
3006 input.Connect(layer->GetInputSlot(0));
3007
3008 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3009}
3010
3011template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003012 typename HalOperation = typename HalPolicy::Operation,
3013 typename HalModel = typename HalPolicy::Model>
3014bool ConvertLogistic(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003015{
Mike Kelly46272802019-08-14 17:00:48 +01003016 armnn::ActivationDescriptor desc;
3017 desc.m_Function = armnn::ActivationFunction::Sigmoid;
3018
3019 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
3020}
3021
3022template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003023 typename HalOperation = typename HalPolicy::Operation,
3024 typename HalModel = typename HalPolicy::Model>
3025bool ConvertMean(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003026{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003027 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003028
3029 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3030 if (!input.IsValid())
3031 {
3032 return Fail("%s: Operation has invalid inputs", __func__);
3033 }
3034
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003035 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003036 if (!output)
3037 {
3038 return Fail("%s: Could not read output 0", __func__);
3039 }
3040
3041 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3042 if (IsDynamicTensor(outputInfo))
3043 {
3044 return Fail("%s: Dynamic output tensors are not supported", __func__);
3045 }
3046
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003047 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Mike Kelly46272802019-08-14 17:00:48 +01003048 if (!axisOperand)
3049 {
3050 return Fail("%s: Could not read input 1", __func__);
3051 }
3052
3053 std::vector<int32_t> axis;
3054 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
3055 {
3056 return Fail("%s: Input 1 has invalid values", __func__);
3057 }
3058
3059 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3060
3061 // Convert the axis to unsigned int and remove duplicates.
3062 unsigned int rank = inputInfo.GetNumDimensions();
3063 std::set<unsigned int> uniqueAxis;
3064 std::transform(axis.begin(), axis.end(),
3065 std::inserter(uniqueAxis, uniqueAxis.begin()),
3066 [rank](int i) -> unsigned int { return (i + rank) % rank; });
3067
3068 // Get the "keep dims" flag.
3069 int32_t keepDims = 0;
3070 if (!GetInputInt32<HalPolicy>(operation, 2, keepDims, model, data))
3071 {
3072 return Fail("%s: Could not read input 2", __func__);
3073 }
3074
3075 armnn::MeanDescriptor descriptor;
3076 descriptor.m_Axis.assign(uniqueAxis.begin(), uniqueAxis.end());
3077 descriptor.m_KeepDims = keepDims > 0;
3078
3079 bool isSupported = false;
3080 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3081 IsMeanSupported,
3082 data.m_Backends,
3083 isSupported,
3084 inputInfo,
3085 outputInfo,
3086 descriptor);
3087 if (!isSupported)
3088 {
3089 return false;
3090 }
3091
3092 armnn::IConnectableLayer* const layer = data.m_Network->AddMeanLayer(descriptor);
3093 assert(layer != nullptr);
3094 input.Connect(layer->GetInputSlot(0));
3095
3096 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3097}
3098
3099template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003100 typename HalOperation = typename HalPolicy::Operation,
3101 typename HalModel = typename HalPolicy::Model>
3102bool ConvertMul(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003103{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003104 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003105
3106 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3107 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
3108
3109 if (!input0.IsValid() || !input1.IsValid())
3110 {
3111 return Fail("%s: Operation has invalid inputs", __func__);
3112 }
3113
3114 // The FuseActivation parameter is always the input index 2
3115 // and it should be optional
3116 ActivationFn activationFunction;
3117 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
3118 {
3119 return Fail("%s: Operation has invalid inputs", __func__);
3120 }
3121
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003122 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003123
3124 if (outputOperand == nullptr)
3125 {
3126 return false;
3127 }
3128
3129 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
3130 if (IsDynamicTensor(outputInfo))
3131 {
3132 return Fail("%s: Dynamic output tensors are not supported", __func__);
3133 }
3134
3135 bool isSupported = false;
3136 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3137 IsMultiplicationSupported,
3138 data.m_Backends,
3139 isSupported,
3140 input0.GetTensorInfo(),
3141 input1.GetTensorInfo(),
3142 outputInfo);
3143 if (!isSupported)
3144 {
3145 return false;
3146 }
3147
3148 armnn::IConnectableLayer* const startLayer = data.m_Network->AddMultiplicationLayer();
3149 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
3150
3151 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
3152 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
3153
3154 if (endLayer != nullptr)
3155 {
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00003156 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01003157 if (!isReshapeSupported)
3158 {
3159 return false;
3160 }
3161
Mike Kelly46272802019-08-14 17:00:48 +01003162 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
3163 }
3164 else
3165 {
3166 return Fail("%s: ProcessActivation failed", __func__);
3167 }
3168}
3169
3170template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003171 typename HalOperation = typename HalPolicy::Operation,
3172 typename HalModel = typename HalPolicy::Model>
3173bool ConvertPad(HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003174{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003175 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003176
Mike Kelly3c673942019-07-25 09:26:06 +01003177 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3178 if (!input.IsValid())
3179 {
3180 return Fail("%s: Operation has invalid inputs", __func__);
3181 }
3182
3183 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3184 unsigned int rank = inputInfo.GetNumDimensions();
3185
3186 armnn::PadDescriptor descriptor;
3187 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
3188 {
3189 return Fail("%s: Could not convert paddings", __func__);
3190 }
3191
3192 // Before Android Q, the pad value for ANEURALNETWORKS_TENSOR_QUANT8_ASYMM was undefined. Since Android Q the pad
3193 // value must be "logical zero" we set it to be equal to the QuantizationOffset so effectively it ends up as
3194 // (QuantizationOffset - QuantizationOffset) * scale = 0.
Derek Lamberti1a38cda2020-01-10 17:28:20 +00003195 if (inputInfo.GetDataType() == armnn::DataType::QAsymmU8)
Mike Kelly3c673942019-07-25 09:26:06 +01003196 {
3197 descriptor.m_PadValue = inputInfo.GetQuantizationOffset();
3198 }
3199
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003200 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly3c673942019-07-25 09:26:06 +01003201 if (!output)
3202 {
3203 return Fail("%s: Could not read output", __func__);
3204 }
3205
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01003206 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kelly3c673942019-07-25 09:26:06 +01003207 if (IsDynamicTensor(outputInfo))
3208 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01003209 return Fail("%s: Dynamic output tensors are not supported", __func__);
Mike Kelly3c673942019-07-25 09:26:06 +01003210 }
3211
3212 bool isSupported = false;
3213 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3214 IsPadSupported,
3215 data.m_Backends,
3216 isSupported,
3217 inputInfo,
3218 outputInfo,
3219 descriptor);
3220 if (!isSupported)
3221 {
3222 return false;
3223 }
3224
3225 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
3226 assert(layer != nullptr);
3227 input.Connect(layer->GetInputSlot(0));
3228 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
3229
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01003230 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +01003231}
3232
Mike Kelly0a879362019-07-29 16:56:31 +01003233template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003234 typename HalOperation = typename HalPolicy::Operation,
3235 typename HalModel = typename HalPolicy::Model>
3236bool ConvertReshape(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003237{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003238 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003239
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003240 const HalOperand* inputOperand = GetInputOperand<HalPolicy>(operation, 0, model);
3241 const HalOperand* requestedShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3242 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003243
3244 if (inputOperand == nullptr
3245 || requestedShapeOperand == nullptr
3246 || outputOperand == nullptr)
3247 {
3248 return Fail("%s: Operation has invalid inputs", __func__);
3249 }
3250
3251 if (requestedShapeOperand->dimensions.size() != 1)
3252 {
3253 return Fail("%s: Input 1 expected to be one-dimensional (found %i dimensions)",
3254 __func__, requestedShapeOperand->dimensions.size());
3255 }
3256
3257 std::vector<int32_t> targetDimensions;
3258 if (!GetTensorInt32Values<HalPolicy>(*requestedShapeOperand, targetDimensions, model, data))
3259 {
3260 return Fail("%s: Could not read values of input 1", __func__);
3261 }
3262
3263 const Shape inputOperandShape = GetOperandShape(*inputOperand);
3264
3265 Shape requestedShape;
3266 // targetDimensions may contain special values (e.g. -1). reshapePrepare() is an AndroidNN provided utility
3267 // function that resolves these values into a fully specified tensor shape.
3268 if (!reshapePrepare(inputOperandShape, targetDimensions.data(), targetDimensions.size(), &requestedShape))
3269 {
3270 return Fail("%s: Failed to resolve the requested shape", __func__);
3271 }
3272
3273 const Shape outputOperandShape = GetOperandShape(*outputOperand);
3274 if (!SameShape(requestedShape, outputOperandShape))
3275 {
3276 return Fail("%s: Shape of output operand does not match resolved requested shape", __func__);
3277 }
3278
3279 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3280 if (!input.IsValid())
3281 {
3282 return Fail("%s: Could not read input 0", __func__);
3283 }
3284
3285 armnn::ReshapeDescriptor reshapeDescriptor;
3286 reshapeDescriptor.m_TargetShape = armnn::TensorShape(requestedShape.dimensions.size(),
3287 requestedShape.dimensions.data());
3288
3289 bool isSupported = false;
3290 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3291 IsReshapeSupported,
3292 data.m_Backends,
3293 isSupported,
3294 input.GetTensorInfo(),
Kevin Mayaed08ac2019-12-12 16:33:31 +00003295 GetTensorInfoForOperand(*outputOperand),
Mike Kelly46272802019-08-14 17:00:48 +01003296 reshapeDescriptor);
3297 if (!isSupported)
3298 {
3299 return false;
3300 }
3301
3302 armnn::IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
3303 assert(layer != nullptr);
3304 input.Connect(layer->GetInputSlot(0));
3305
3306 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3307}
3308
3309template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003310 typename HalOperation = typename HalPolicy::Operation,
3311 typename HalModel = typename HalPolicy::Model>
3312bool ConvertSub(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly0a879362019-07-29 16:56:31 +01003313{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003314 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003315
Mike Kelly0a879362019-07-29 16:56:31 +01003316 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3317 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
3318
3319 if (!input0.IsValid() || !input1.IsValid())
3320 {
3321 return Fail("%s: Operation has invalid inputs", __func__);
3322 }
3323
3324 // The FuseActivation parameter is always the input index 2
3325 // and it should be optional
3326 ActivationFn activationFunction;
3327 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
3328 {
3329 return Fail("%s: Operation has invalid inputs", __func__);
3330 }
3331
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003332 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly0a879362019-07-29 16:56:31 +01003333 if (!output)
3334 {
3335 return Fail("%s: Could not read output 0", __func__);
3336 }
3337
3338 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3339 if (IsDynamicTensor(outputInfo))
3340 {
3341 return Fail("%s: Dynamic output tensors are not supported", __func__);
3342 }
3343
3344 bool isSupported = false;
3345 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3346 IsSubtractionSupported,
3347 data.m_Backends,
3348 isSupported,
3349 input0.GetTensorInfo(),
3350 input1.GetTensorInfo(),
3351 outputInfo);
3352 if (!isSupported)
3353 {
3354 return false;
3355 }
3356
3357 armnn::IConnectableLayer* const startLayer = data.m_Network->AddSubtractionLayer();
3358 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
3359
3360 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
3361 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
3362
3363 if (endLayer)
3364 {
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00003365 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01003366 if (!isReshapeSupported)
3367 {
3368 return false;
3369 }
Mike Kelly0a879362019-07-29 16:56:31 +01003370 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
3371 }
3372
3373 return Fail("%s: ProcessActivation failed", __func__);
3374}
3375
Finn Williams23b87b32019-07-30 11:44:05 +01003376template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003377 typename HalOperation = typename HalPolicy::Operation,
3378 typename HalModel = typename HalPolicy::Model>
3379bool ConvertSqueeze(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003380{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003381 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003382
3383 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3384 if (!input.IsValid())
3385 {
3386 return Fail("%s: Operation has invalid inputs", __func__);
3387 }
3388
3389 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3390 unsigned int rank = inputInfo.GetNumDimensions();
3391 if (rank > 4)
3392 {
3393 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3394 }
3395
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003396 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003397 if (!output)
3398 {
3399 return Fail("%s: Could not read output 0", __func__);
3400 }
3401
3402 if (IsDynamicTensor(GetTensorInfoForOperand(*output)))
3403 {
3404 return Fail("%s: Dynamic output tensors are not supported", __func__);
3405 }
3406
3407 // NOTE: Axis is an optional parameter to SQUEEZE, therefore we do not want to generate a failure
3408 // if the operand index is out of bounds.
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003409 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model, false);
Mike Kelly46272802019-08-14 17:00:48 +01003410
3411 const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
3412
3413 std::vector<int32_t> axis;
3414 if (!axisOperand)
3415 {
3416 axis.assign(dimensionSequence,
3417 dimensionSequence + rank);
3418 }
Mike Kellyeec836e2020-02-18 10:03:30 +00003419 else if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
Mike Kelly46272802019-08-14 17:00:48 +01003420 {
Mike Kellyeec836e2020-02-18 10:03:30 +00003421 return Fail("%s: Operation has an invalid or unsupported axis operand", __func__);
Mike Kelly46272802019-08-14 17:00:48 +01003422 }
3423
3424 std::vector<uint32_t> outputDims;
3425 for (unsigned int i = 0; i < rank; i++)
3426 {
3427 bool skipSqueeze = (std::find(axis.begin(), axis.end(), i) == axis.end());
3428 auto currentDimension = inputInfo.GetShape()[i];
3429 if (skipSqueeze || currentDimension != 1)
3430 {
3431 outputDims.push_back(currentDimension);
3432 }
3433 }
3434
3435 armnn::TensorShape outShape = armnn::TensorShape(outputDims.size(), outputDims.data());
3436
3437 armnn::TensorInfo outputInfo = inputInfo;
3438 outputInfo.SetShape(outShape);
3439
3440 armnn::ReshapeDescriptor reshapeDesc;
3441 reshapeDesc.m_TargetShape = outputInfo.GetShape();
3442
3443 bool isSupported = false;
3444 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3445 IsReshapeSupported,
3446 data.m_Backends,
3447 isSupported,
3448 inputInfo,
Kevin Mayaed08ac2019-12-12 16:33:31 +00003449 outputInfo,
Mike Kelly46272802019-08-14 17:00:48 +01003450 reshapeDesc);
3451 if (!isSupported)
3452 {
3453 return false;
3454 }
3455
3456 armnn::IConnectableLayer* const layer = data.m_Network->AddReshapeLayer(reshapeDesc);
3457 assert(layer != nullptr);
3458 input.Connect(layer->GetInputSlot(0));
3459
3460 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3461}
3462
3463template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003464 typename HalOperation = typename HalPolicy::Operation,
3465 typename HalModel = typename HalPolicy::Model>
3466bool ConvertStridedSlice(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003467{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003468 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003469
3470 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3471 if (!input.IsValid())
3472 {
3473 return Fail("%s: Operation has invalid inputs", __func__);
3474 }
3475
3476 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3477 unsigned int rank = inputInfo.GetNumDimensions();
3478 if (rank > 4)
3479 {
3480 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3481 }
3482
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003483 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003484 if (!output)
3485 {
3486 return Fail("%s: Could not read output 0", __func__);
3487 }
3488
3489 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3490 if (IsDynamicTensor(outputInfo))
3491 {
3492 return Fail("%s: Dynamic output tensors are not supported", __func__);
3493 }
3494
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003495 const HalOperand* beginOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3496 const HalOperand* endOperand = GetInputOperand<HalPolicy>(operation, 2, model);
3497 const HalOperand* stridesOperand = GetInputOperand<HalPolicy>(operation, 3, model);
Mike Kelly46272802019-08-14 17:00:48 +01003498
3499 std::vector<int32_t> beginValues;
3500 std::vector<int32_t> endValues;
3501 std::vector<int32_t> stridesValues;
3502
3503 // The length of the beginOperand, endOperand and stridesOperand must be of a rank(input)
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003504 auto ValidateInputOperands = [&] (const HalOperand& operand, std::vector<int32_t>& operandValues)
Mike Kelly46272802019-08-14 17:00:48 +01003505 {
3506 if (!GetTensorInt32Values<HalPolicy>(operand, operandValues, model, data))
3507 {
3508 return false;
3509 }
3510
3511 if (operandValues.size() != rank)
3512 {
3513 return false;
3514 }
3515
3516 return true;
3517 };
3518
3519 if (!ValidateInputOperands(*beginOperand, beginValues)
3520 || !ValidateInputOperands(*endOperand, endValues)
3521 || !ValidateInputOperands(*stridesOperand, stridesValues))
3522 {
3523 return Fail("%s: Operation has invalid input operand", __func__);
3524 }
3525
3526 // Stride cannot have value '0'
3527 if (std::any_of(stridesValues.cbegin(), stridesValues.cend(), [](int32_t i){ return i == 0; }))
3528 {
3529 return Fail("%s: Stride must be non-zero value.", __func__);
3530 }
3531
3532 armnn::StridedSliceDescriptor descriptor;
3533 descriptor.m_Begin.assign(beginValues.cbegin(), beginValues.cend());
3534 descriptor.m_End.assign(endValues.cbegin(), endValues.cend());
3535 descriptor.m_Stride.assign(stridesValues.cbegin(), stridesValues.cend());
3536 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
3537
3538 // Get the "begin_mask", "end_mask", and "shrink_axis_mask" flags
3539 if (!GetInputInt32<HalPolicy>(operation, 4, descriptor.m_BeginMask, model, data) ||
3540 !GetInputInt32<HalPolicy>(operation, 5, descriptor.m_EndMask, model, data) ||
3541 !GetInputInt32<HalPolicy>(operation, 6, descriptor.m_ShrinkAxisMask, model, data))
3542 {
3543 return Fail("%s: Operation has invalid inputs", __func__);
3544 }
3545
3546 bool isSupported = false;
3547 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3548 IsStridedSliceSupported,
3549 data.m_Backends,
3550 isSupported,
3551 inputInfo,
3552 outputInfo,
3553 descriptor);
3554 if (!isSupported)
3555 {
3556 return false;
3557 }
3558
3559 armnn::IConnectableLayer* const layer = data.m_Network->AddStridedSliceLayer(descriptor);
3560 assert(layer != nullptr);
3561 input.Connect(layer->GetInputSlot(0));
3562
3563 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3564}
3565
3566template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003567 typename HalOperation = typename HalPolicy::Operation,
3568 typename HalModel = typename HalPolicy::Model>
3569bool ConvertTranspose(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003570{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003571 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003572
3573 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3574 if (!input.IsValid())
3575 {
3576 return Fail("%s: Operation has invalid inputs", __func__);
3577 }
3578
3579 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3580 unsigned int rank = inputInfo.GetNumDimensions();
3581 if (rank > 4)
3582 {
3583 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3584 }
3585
3586 // NOTE: Axis is an optional parameter to TRANSPOSE, therefore we do not want to generate a failure
3587 // if the operand index is out of bounds.
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003588 const HalOperand* permOperand = GetInputOperand<HalPolicy>(operation, 1, model, false);
Mike Kelly46272802019-08-14 17:00:48 +01003589
3590 std::vector<int32_t> perm(rank);
3591 if (!permOperand)
3592 {
3593 // NOTE: If perm is not given, it is set to (n-1...0), where n is the rank of the tensor
3594 for (unsigned int i = rank; i > 0; i--)
3595 {
3596 perm[rank - i] = boost::numeric_cast<int> (i - 1);
3597 }
3598 }
Mike Kellyeec836e2020-02-18 10:03:30 +00003599 else if (!GetTensorInt32Values<HalPolicy>(*permOperand, perm, model, data))
Mike Kelly46272802019-08-14 17:00:48 +01003600 {
Mike Kellyeec836e2020-02-18 10:03:30 +00003601 return Fail("%s: Operation has an invalid or unsupported permutation operand", __func__);
Mike Kelly46272802019-08-14 17:00:48 +01003602 }
3603
3604 std::vector<uint32_t> outputDims(perm.begin(), perm.begin() + rank);
3605
Mike Kelly4a956582020-02-28 10:32:09 +00003606 armnn::TransposeDescriptor transposeDesc;
3607 transposeDesc.m_DimMappings = armnn::PermutationVector(outputDims.data(), outputDims.size());
Mike Kelly46272802019-08-14 17:00:48 +01003608
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003609 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003610 if (!output)
3611 {
3612 return Fail("%s: Could not read output 0", __func__);
3613 }
3614
3615 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Matthew Bentham0182fd32019-12-06 09:45:13 +00003616 if (IsDynamicTensor(outputInfo))
3617 {
3618 return Fail("%s: Dynamic output tensors are not supported", __func__);
3619 }
3620
Mike Kelly46272802019-08-14 17:00:48 +01003621
3622 bool isSupported = false;
3623 FORWARD_LAYER_SUPPORT_FUNC(__func__,
Mike Kelly4a956582020-02-28 10:32:09 +00003624 IsTransposeSupported,
Mike Kelly46272802019-08-14 17:00:48 +01003625 data.m_Backends,
3626 isSupported,
3627 inputInfo,
3628 outputInfo,
Mike Kelly4a956582020-02-28 10:32:09 +00003629 transposeDesc);
Mike Kelly46272802019-08-14 17:00:48 +01003630 if (!isSupported)
3631 {
3632 return false;
3633 }
3634
Mike Kelly4a956582020-02-28 10:32:09 +00003635 armnn::IConnectableLayer* const layer = data.m_Network->AddTransposeLayer(transposeDesc);
Mike Kelly46272802019-08-14 17:00:48 +01003636 assert(layer != nullptr);
3637 input.Connect(layer->GetInputSlot(0));
3638
3639 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3640}
3641
3642template<typename HalPolicy,
Finn Williams23b87b32019-07-30 11:44:05 +01003643 typename HalOperation = typename HalPolicy::Operation,
Finn Williams0e4e4392019-07-31 10:56:27 +01003644 typename HalOperand = typename HalPolicy::Operand,
Finn Williams23b87b32019-07-30 11:44:05 +01003645 typename HalModel = typename HalPolicy::Model>
3646bool ConvertBatchToSpaceNd(const HalOperation& operation,
3647 const HalModel& model,
3648 ConversionData& data)
3649{
Finn Williams23b87b32019-07-30 11:44:05 +01003650
3651 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3652 if (!input.IsValid())
3653 {
3654 return Fail("%s: Operation has invalid inputs", __func__);
3655 }
3656
3657 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3658 if (!output)
3659 {
3660 return Fail("%s: Could not read output 0", __func__);
3661 }
3662
3663 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3664 if (IsDynamicTensor(outputInfo))
3665 {
3666 return Fail("%s: Dynamic output tensors are not supported", __func__);
3667 }
3668
3669 const HalOperand* blockOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3670 if (!blockOperand)
3671 {
3672 return Fail("%s: Could not read input 1", __func__);
3673 }
3674
3675 // Convert the block operand to int32
3676 std::vector<int32_t> block;
3677 if (!GetTensorInt32Values<HalPolicy>(*blockOperand, block, model, data))
3678 {
3679 return Fail("%s: Input 1 has invalid values", __func__);
3680 }
3681
3682 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3683
3684 unsigned int rank = inputInfo.GetNumDimensions();
3685 if (rank != 4)
3686 {
3687 Fail("%s: Only inputs with rank equal to 4 are supported", __func__);
3688 }
3689
3690 if (std::any_of(block.cbegin(), block.cend(), [](int32_t i){ return i < 1; }))
3691 {
3692 return Fail("%s: Block sizes for each spatial dimension of the input tensor must be"
3693 " greater than or equal to 1", __func__);
3694 }
3695
3696 armnn::BatchToSpaceNdDescriptor batchToSpaceNdDesc;
3697 batchToSpaceNdDesc.m_BlockShape.assign(block.cbegin(), block.cend());
3698 batchToSpaceNdDesc.m_DataLayout = armnn::DataLayout::NHWC;
3699
Kevin May42477c12020-03-26 13:34:14 +00003700 if (Is12OrLaterOperand(*output))
Finn Williams23b87b32019-07-30 11:44:05 +01003701 {
Finn Williams0e4e4392019-07-31 10:56:27 +01003702 batchToSpaceNdDesc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +01003703 }
3704 // Setting crops to 0,0 0,0 as it is not supported in Android NN API
3705 batchToSpaceNdDesc.m_Crops = {{0, 0}, {0, 0}};
3706
3707 bool isSupported = false;
3708 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3709 IsBatchToSpaceNdSupported,
3710 data.m_Backends,
3711 isSupported,
3712 inputInfo,
3713 outputInfo,
3714 batchToSpaceNdDesc);
3715 if (!isSupported)
3716 {
3717 return false;
3718 }
3719
3720 armnn::IConnectableLayer* const layer = data.m_Network->AddBatchToSpaceNdLayer(batchToSpaceNdDesc);
3721 assert(layer != nullptr);
3722 input.Connect(layer->GetInputSlot(0));
3723
3724 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3725}
Mike Kelly0a879362019-07-29 16:56:31 +01003726
Finn Williamsd74c5052019-07-30 17:06:00 +01003727template<typename HalPolicy,
3728 typename HalOperation = typename HalPolicy::Operation,
3729 typename HalOperand = typename HalPolicy::Operand,
3730 typename HalModel = typename HalPolicy::Model>
3731bool ConvertSpaceToBatchNd(const HalOperation& operation, const HalModel& model, ConversionData& data)
3732{
3733 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3734 if (!input.IsValid())
3735 {
3736 return Fail("%s: Operation has invalid inputs", __func__);
3737 }
3738
3739 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3740 unsigned int rank = inputInfo.GetNumDimensions();
3741 unsigned int spatialDim = rank - 2;
3742
3743 if (rank != 4)
3744 {
3745 Fail("%s: Only inputs with rank 4 are supported", __func__);
3746 }
3747
3748 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3749 if (!output)
3750 {
3751 return Fail("%s: Could not read output 0", __func__);
3752 }
3753
3754 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3755 if (IsDynamicTensor(outputInfo))
3756 {
3757 return Fail("%s: Dynamic output tensors are not supported", __func__);
3758 }
3759
3760 const HalOperand* blockShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3761 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 2, model);
3762
3763 armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand(*blockShapeOperand);
3764 if (blockShapeOperandShape.GetNumDimensions() != 1 || blockShapeOperandShape.GetNumElements() != spatialDim)
3765 {
3766 return Fail("%s: Operation has invalid block shape operand: expected shape [%d]", __func__, spatialDim);
3767 }
3768
3769 std::vector<int32_t> blockShape;
Mike Kellyeec836e2020-02-18 10:03:30 +00003770 if (!GetTensorInt32Values<HalPolicy>(*blockShapeOperand, blockShape, model, data))
3771 {
3772 return Fail("%s: Operation has an invalid or unsupported block size operand", __func__);
3773 }
Finn Williamsd74c5052019-07-30 17:06:00 +01003774 if (std::any_of(blockShape.cbegin(), blockShape.cend(), [](int32_t i){ return i < 1; }))
3775 {
3776 return Fail("%s: Block shape must be at least 1 in all dimensions.", __func__);
3777 }
3778
3779 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
3780 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != 2 * spatialDim)
3781 {
3782 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, spatialDim);
3783 }
3784
3785 std::vector<std::pair<unsigned int, unsigned int>> paddingList;
3786 std::vector<int32_t> paddings;
Mike Kellyeec836e2020-02-18 10:03:30 +00003787 if (!GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data))
3788 {
3789 return Fail("%s: Operation has an invalid or unsupported paddings operand", __func__);
3790 }
Finn Williamsd74c5052019-07-30 17:06:00 +01003791 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
3792 {
3793 int paddingBeforeInput = paddings[i];
3794 int paddingAfterInput = paddings[i + 1];
3795 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
3796 {
3797 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
3798 }
3799
3800 paddingList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
3801 }
3802
3803 armnn::SpaceToBatchNdDescriptor descriptor;
3804 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
3805 descriptor.m_BlockShape.assign(blockShape.cbegin(), blockShape.cend());
3806 descriptor.m_PadList.assign(paddingList.cbegin(), paddingList.cend());
3807
Kevin May42477c12020-03-26 13:34:14 +00003808 if (Is12OrLaterOperand(*output))
Finn Williamsd74c5052019-07-30 17:06:00 +01003809 {
3810 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
3811 }
3812
3813 bool isSupported = false;
3814 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3815 IsSpaceToBatchNdSupported,
3816 data.m_Backends,
3817 isSupported,
3818 inputInfo,
3819 outputInfo,
3820 descriptor);
3821 if (!isSupported)
3822 {
3823 return false;
3824 }
3825
3826 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToBatchNdLayer(descriptor);
3827 assert(layer != nullptr);
3828 input.Connect(layer->GetInputSlot(0));
3829
3830 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3831}
3832
saoste01b8471482018-10-10 09:44:51 +01003833} // namespace armnn_driver