blob: 90b1c7de3bc67842181d8ecd1cadd04aec6a3cbf [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>
Jan Eilers0b7a4192020-03-09 18:20:42 +000013#include <armnn/utility/IgnoreUnused.hpp>
arovir01b0717b52018-09-05 17:03:25 +010014
Matteo Martincigh00d6ed12019-11-28 17:13:24 +000015#include <armnnUtils/DataLayoutIndexed.hpp>
Mike Kelly4a956582020-02-28 10:32:09 +000016#include <armnnUtils/Transpose.hpp>
arovir01b0717b52018-09-05 17:03:25 +010017
Mike Kelly46272802019-08-14 17:00:48 +010018#include "1.0/FullyConnected.hpp"
19
arovir01b0717b52018-09-05 17:03:25 +010020#include <ActivationFunctor.h>
21#include <CpuExecutor.h>
22#include <OperationsUtils.h>
23
24#include <boost/assert.hpp>
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
Mike Kellyb5fdf382019-06-11 16:35:25 +0100186#ifdef ARMNN_ANDROID_NN_V1_2
187
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
204inline bool IsBool(V1_0::Operand)
205{
206 return false;
207}
208
Sadik Armagan61113162019-07-25 09:09:40 +0100209inline bool Is12Operand(V1_0::Operand)
210{
211 return false;
212}
213
Mike Kellyb5fdf382019-06-11 16:35:25 +0100214#ifdef ARMNN_ANDROID_NN_V1_2
215
216inline bool IsBool(V1_2::Operand operand)
217{
218 return operand.type == V1_2::OperandType::BOOL;
219}
220
Sadik Armagan61113162019-07-25 09:09:40 +0100221/// Checks if a operand is 1_2 Operand
222inline bool Is12Operand(V1_2::Operand)
223{
224 return true;
225}
226
Mike Kellyb5fdf382019-06-11 16:35:25 +0100227#endif
228
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100229template<typename LayerHandleType>
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +0000230armnn::IConnectableLayer& AddReshapeLayer(armnn::INetwork& network,
231 LayerHandleType& inputLayer,
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100232 armnn::TensorInfo reshapeInfo)
233{
234 armnn::ReshapeDescriptor reshapeDescriptor;
235 reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
236
237 armnn::IConnectableLayer* reshapeLayer = network.AddReshapeLayer(reshapeDescriptor);
238 BOOST_ASSERT(reshapeLayer != nullptr);
239
240 // Attach the input layer to the reshape layer
241 inputLayer.Connect(reshapeLayer->GetInputSlot(0));
242 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapeInfo);
243
244 return *reshapeLayer;
245}
246
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +0000247bool BroadcastTensor(LayerInputHandle& input0,
248 LayerInputHandle& input1,
249 armnn::IConnectableLayer* startLayer,
250 ConversionData& data)
arovir01b0717b52018-09-05 17:03:25 +0100251{
252 BOOST_ASSERT(startLayer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +0100253
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100254 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
255 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
256
257 unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
258 unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
259
260 if (inputDimensions0 == inputDimensions1)
arovir01b0717b52018-09-05 17:03:25 +0100261 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100262 // The inputs have the same number of dimensions, simply connect them to the given layer as they are
263 input0.Connect(startLayer->GetInputSlot(0));
264 input1.Connect(startLayer->GetInputSlot(1));
265
Sadik Armagan64b19b52019-08-19 09:49:58 +0100266 return true;
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100267 }
268
269 // Since the number of dimensions do not match then we need to add degenerate dimensions
270 // to the "smaller" tensor using a reshape, while keeping the order of the inputs.
271
272 unsigned int maxInputDimensions = std::max(inputDimensions0, inputDimensions1);
273 unsigned int sizeDifference = std::abs(boost::numeric_cast<int>(inputDimensions0) -
274 boost::numeric_cast<int>(inputDimensions1));
275
276 bool input0IsSmaller = inputDimensions0 < inputDimensions1;
277 LayerInputHandle& smallInputHandle = input0IsSmaller ? input0 : input1;
278 const armnn::TensorInfo& smallInfo = smallInputHandle.GetTensorInfo();
279
280 const armnn::TensorShape& smallShape = smallInfo.GetShape();
281 std::vector<unsigned int> reshapedDimensions(maxInputDimensions, 1);
282 for (unsigned int i = sizeDifference; i < maxInputDimensions; i++)
283 {
284 reshapedDimensions[i] = smallShape[i - sizeDifference];
285 }
286
287 armnn::TensorInfo reshapedInfo = smallInfo;
288 reshapedInfo.SetShape(armnn::TensorShape{ boost::numeric_cast<unsigned int>(reshapedDimensions.size()),
289 reshapedDimensions.data() });
Sadik Armagan64b19b52019-08-19 09:49:58 +0100290
291 // RehsapeDescriptor that is ignored in the IsReshapeSupported function
292 armnn::ReshapeDescriptor reshapeDescriptor;
293
294 bool isSupported = false;
295 FORWARD_LAYER_SUPPORT_FUNC(__func__,
296 IsReshapeSupported,
297 data.m_Backends,
298 isSupported,
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +0000299 smallInfo,
Sadik Armagan64b19b52019-08-19 09:49:58 +0100300 reshapedInfo,
301 reshapeDescriptor);
302 if (!isSupported)
303 {
304 return false;
305 }
306
307 BOOST_ASSERT(data.m_Network != nullptr);
308 armnn::IConnectableLayer& reshapeLayer = AddReshapeLayer(*data.m_Network, smallInputHandle, reshapedInfo);
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100309
310 if (input0IsSmaller)
311 {
312 // Input0 is the "smaller" tensor, connect the reshape layer as follows:
313 //
314 // Input0 Input1
arovir01b0717b52018-09-05 17:03:25 +0100315 // | |
316 // Reshape |
317 // \ /
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100318 // StartLayer
arovir01b0717b52018-09-05 17:03:25 +0100319
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100320 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
321 input1.Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100322 }
323 else
324 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100325 // Input1 is the "smaller" tensor, connect the reshape layer as follows:
326 //
327 // Input0 Input1
328 // | |
329 // | Reshape
330 // \ /
331 // StartLayer
332
arovir01b0717b52018-09-05 17:03:25 +0100333 input0.Connect(startLayer->GetInputSlot(0));
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100334 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100335 }
Sadik Armagan64b19b52019-08-19 09:49:58 +0100336
337 return true;
arovir01b0717b52018-09-05 17:03:25 +0100338}
339
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +0000340void CalcPadding(uint32_t input,
341 uint32_t kernel,
342 uint32_t stride,
343 uint32_t& outPadHead,
344 uint32_t& outPadTail,
arovir01b0717b52018-09-05 17:03:25 +0100345 android::nn::PaddingScheme scheme)
346{
347 int32_t padHead;
348 int32_t padTail;
349 calculateExplicitPadding(input, stride, kernel, scheme, &padHead, &padTail);
350 outPadHead = boost::numeric_cast<uint32_t>(padHead);
351 outPadTail = boost::numeric_cast<uint32_t>(padTail);
352}
353
Mike Kelly86b36d42019-07-12 16:39:33 +0100354#ifdef ARMNN_ANDROID_NN_V1_2
355
356void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t dilation, uint32_t& outPadHead,
357 uint32_t& outPadTail, android::nn::PaddingScheme scheme)
358{
359 int32_t padHead;
360 int32_t padTail;
361 calculateExplicitPadding(input, stride, dilation, kernel, scheme, &padHead, &padTail);
362 outPadHead = boost::numeric_cast<uint32_t>(padHead);
363 outPadTail = boost::numeric_cast<uint32_t>(padTail);
364}
365
Mike Kelly26123db2020-01-15 10:02:33 +0000366void CalcPaddingTransposeConv(uint32_t output, uint32_t kernel, int32_t stride, int32_t& outPadHead,
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +0100367 int32_t& outPadTail, android::nn::PaddingScheme scheme)
368{
369 calculateExplicitPaddingTransposeConv(output, stride, kernel, scheme, &outPadHead, &outPadTail);
370}
371
Mike Kelly86b36d42019-07-12 16:39:33 +0100372#endif
373
Matthew Bentham912b3622019-05-03 15:49:14 +0100374Shape GetOperandShape(const V1_0::Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100375{
376 Shape shape;
Matthew Bentham912b3622019-05-03 15:49:14 +0100377 shape.type = OperandType(operand.type);
arovir01b0717b52018-09-05 17:03:25 +0100378 shape.dimensions = operand.dimensions;
379 shape.scale = operand.scale;
380 shape.offset = operand.zeroPoint;
381 return shape;
382}
383
Mike Kelly46272802019-08-14 17:00:48 +0100384#ifdef ARMNN_ANDROID_NN_V1_2
385
386Shape GetOperandShape(const V1_2::Operand& operand)
387{
388 Shape shape;
389 shape.type = OperandType(operand.type);
390 shape.dimensions = operand.dimensions;
391 shape.scale = operand.scale;
392 shape.offset = operand.zeroPoint;
393 return shape;
394}
395
396#endif
397
arovir01b0717b52018-09-05 17:03:25 +0100398// ArmNN requires the bias scale to be equal to the product of the weight and input scales, which is also
399// 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 +0100400// we accept some tolerance. We don't want ArmNN itself to accept these inconsistencies as it is up to the
401// user (us, in this case) to ensure they match.
arovir01b0717b52018-09-05 17:03:25 +0100402void SanitizeBiasQuantizationScale(armnn::TensorInfo& biasInfo,
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000403 const armnn::TensorInfo& weightInfo,
404 const armnn::TensorInfo& inputInfo)
arovir01b0717b52018-09-05 17:03:25 +0100405{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000406 if (weightInfo.HasPerAxisQuantization())
arovir01b0717b52018-09-05 17:03:25 +0100407 {
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000408 // NOTE: Bias scale is always set to 0 for per-axis quantization and
409 // it needs to be calculated: scale[i] = input_scale * weight_scale[i]
410 auto UpdateBiasScaleValue = [&inputInfo](float biasScale) -> float
arovir01b0717b52018-09-05 17:03:25 +0100411 {
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000412 return biasScale * inputInfo.GetQuantizationScale();
413 };
414
415 std::vector<float> biasScales(weightInfo.GetQuantizationScales());
416 std::transform(biasScales.begin(), biasScales.end(), biasScales.begin(), UpdateBiasScaleValue);
417
418 biasInfo.SetQuantizationScales(biasScales);
419 biasInfo.SetQuantizationDim(weightInfo.GetQuantizationDim());
420
421 ALOGV("Bias quantization params have been updated for per-axis quantization");
422 }
423 else
424 {
425 const float expectedBiasScale = weightInfo.GetQuantizationScale() * inputInfo.GetQuantizationScale();
426 if (biasInfo.GetQuantizationScale() != expectedBiasScale)
427 {
428 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
429 if (comparer(biasInfo.GetQuantizationScale(), expectedBiasScale))
430 {
431 ALOGW("Bias quantization scale has been modified to match input * weights");
432 biasInfo.SetQuantizationScale(expectedBiasScale);
433 }
arovir01b0717b52018-09-05 17:03:25 +0100434 }
435 }
436}
437
438// 4D Tensor Permutations
439const armnn::PermutationVector IdentityPermutation4D({ 0U, 1U, 2U, 3U });
arovir01b0717b52018-09-05 17:03:25 +0100440const armnn::PermutationVector SwapDim1And2({ 0U, 2U, 1U, 3U });
441
442// 3D Permutation Vectors
Mike Kelly4a956582020-02-28 10:32:09 +0000443const armnn::PermutationVector RotateTensorLeft({ 1U, 2U, 0U });
444const armnn::PermutationVector RotateTensorRight({ 2U, 0U, 1U });
arovir01b0717b52018-09-05 17:03:25 +0100445
446template<typename OSlot>
Mike Kelly4a956582020-02-28 10:32:09 +0000447armnn::IConnectableLayer& AddTransposeLayer(armnn::INetwork& network, OSlot& input,
448 const armnn::PermutationVector& mappings)
arovir01b0717b52018-09-05 17:03:25 +0100449{
450 // Add swizzle layer
Mike Kelly4a956582020-02-28 10:32:09 +0000451 armnn::IConnectableLayer* const layer = network.AddTransposeLayer(mappings);
arovir01b0717b52018-09-05 17:03:25 +0100452
453 BOOST_ASSERT(layer != nullptr);
454
455 // Connect input to swizzle layer
456 input.Connect(layer->GetInputSlot(0));
457
458 // Setup swizzled output
Mike Kelly4a956582020-02-28 10:32:09 +0000459 const armnn::TensorInfo outInfo = armnnUtils::TransposeTensorShape(input.GetTensorInfo(), mappings);
arovir01b0717b52018-09-05 17:03:25 +0100460 layer->GetOutputSlot(0).SetTensorInfo(outInfo);
461
462 return *layer;
463}
464
arovir01b0717b52018-09-05 17:03:25 +0100465bool ValidateConcatOutputShape(const std::vector<armnn::TensorShape> & inputShapes,
466 const armnn::TensorShape & outputShape,
467 uint32_t concatDim)
468{
469 // Validate the output shape is correct given the input shapes (which have just been validated)
470 unsigned int numDimensions = inputShapes[0].GetNumDimensions();
471 if (outputShape.GetNumDimensions() != numDimensions)
472 {
473 return Fail("%s: Output shape has wrong number of dimensions", __func__);
474 }
475
476 unsigned int outputSizeAlongConcatenatedDimension = 0;
477 for (unsigned int i = 0; i < inputShapes.size(); i++)
478 {
479 outputSizeAlongConcatenatedDimension += inputShapes[i][concatDim];
480 }
481
482 for (unsigned int i = 0; i < numDimensions; ++i)
483 {
484 if (i == concatDim)
485 {
486 if (outputShape[i] != outputSizeAlongConcatenatedDimension)
487 {
488 return Fail(
489 "%s: Invalid output shape for dimension %d (%d != %d)",
490 __func__,
491 i,
492 outputShape[i],
493 outputSizeAlongConcatenatedDimension);
494 }
495 }
496 else
497 {
498 if (outputShape[i] != inputShapes[0][i])
499 {
500 return Fail("%s: Invalid output shape", __func__);
501 }
502 }
503 }
504
505 return true;
506}
507
508bool RequiresReshape(armnn::TensorShape & inputShape)
509{
510 return inputShape.GetNumDimensions() < 3;
511}
512
arovir01b0717b52018-09-05 17:03:25 +0100513void SwizzleInputs(armnn::INetwork& network,
514 std::vector<LayerInputHandle>& inputs,
515 std::vector<armnn::TensorShape>& inputShapes,
516 const armnn::PermutationVector& mapping)
517{
518 if (!mapping.IsEqual(IdentityPermutation4D))
519 {
520 size_t nInputs = inputs.size();
521 for (size_t i=0; i<nInputs; ++i)
522 {
523 // add swizzle layer
Mike Kelly4a956582020-02-28 10:32:09 +0000524 armnn::IConnectableLayer& swizzleLayer = AddTransposeLayer(network, inputs[i], mapping);
arovir01b0717b52018-09-05 17:03:25 +0100525 auto& outputSlot = swizzleLayer.GetOutputSlot(0);
526 auto& outputInfo = outputSlot.GetTensorInfo();
527 // replace inputs with the swizzled ones
528 inputs[i] = LayerInputHandle(true, &outputSlot, outputInfo);
529 inputShapes[i] = inputs[i].GetTensorInfo().GetShape();
530 }
531 }
532}
533
Kevin Mayaed08ac2019-12-12 16:33:31 +0000534bool CheckReshapeSupported(ConversionData& data,
535 std::vector<LayerInputHandle>& inputs,
536 std::vector<armnn::TensorShape>& inputShapes,
537 const armnn::PermutationVector& mapping,
538 const armnn::TensorInfo& outputInfo)
539{
540 if (!mapping.IsEqual(IdentityPermutation4D))
541 {
542 size_t nInputs = inputs.size();
543 for (size_t i=0; i<nInputs; ++i)
544 {
545 // check permute layer
Mike Kelly4a956582020-02-28 10:32:09 +0000546 armnn::TransposeDescriptor transposeDesc;
547 transposeDesc.m_DimMappings = mapping;
Kevin Mayaed08ac2019-12-12 16:33:31 +0000548
549 bool isSupported = false;
550 FORWARD_LAYER_SUPPORT_FUNC(__func__,
Mike Kelly4a956582020-02-28 10:32:09 +0000551 IsTransposeSupported,
Kevin Mayaed08ac2019-12-12 16:33:31 +0000552 data.m_Backends,
553 isSupported,
554 inputs[i].GetTensorInfo(),
555 outputInfo,
Mike Kelly4a956582020-02-28 10:32:09 +0000556 transposeDesc);
Kevin Mayaed08ac2019-12-12 16:33:31 +0000557 if (!isSupported)
558 {
559 return false;
560 }
561
562 }
563 SwizzleInputs(*data.m_Network, inputs, inputShapes, mapping);
564 }
565 return true;
566}
567
568
narpra01f176d5a2018-11-18 20:17:48 +0000569bool CreateConcatPermutationParameters(const unsigned int numberOfDimensions,
570 int32_t & concatDimension,
571 std::pair<armnn::PermutationVector, armnn::PermutationVector> & permutationPair)
arovir01b0717b52018-09-05 17:03:25 +0100572{
narpra01f176d5a2018-11-18 20:17:48 +0000573 bool needPermute = false;
arovir01b0717b52018-09-05 17:03:25 +0100574 BOOST_ASSERT(numberOfDimensions >= 3);
575
576 // ArmNN uses Compute Library subtensors to perform concatenation
narpra01f176d5a2018-11-18 20:17:48 +0000577 // This only works when concatenating along dimension 0, 1 or 3 for a 4-D tensor,
578 // or along dimension 0 or 2 for a 3-D tensor.
579 if (numberOfDimensions == 4 && concatDimension == 2)
arovir01b0717b52018-09-05 17:03:25 +0100580 {
narpra01f176d5a2018-11-18 20:17:48 +0000581 concatDimension = 1;
582 permutationPair = std::make_pair(SwapDim1And2, SwapDim1And2);
583 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100584 }
narpra01f176d5a2018-11-18 20:17:48 +0000585 else if (numberOfDimensions == 3 && concatDimension == 1)
arovir01b0717b52018-09-05 17:03:25 +0100586 {
narpra01f176d5a2018-11-18 20:17:48 +0000587 concatDimension = 0;
588 permutationPair = std::make_pair(RotateTensorLeft, RotateTensorRight);
589 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100590 }
narpra01f176d5a2018-11-18 20:17:48 +0000591 return needPermute;
arovir01b0717b52018-09-05 17:03:25 +0100592}
593
594} // anonymous namespace
595
596namespace armnn_driver
597{
598
599//// Creates an ArmNN activation layer and connects it to the given layer, if the
600//// passed in AndroidNN activation function requires so.
601//// @return The end layer of the sequence of layers built for the given AndroidNN
602//// activation function or nullptr if an error occurred (e.g. unsupported activation).
603//// Note that the end layer matches the input layer if no activation is required
604//// (the sequence of layers has length 1).
605armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
606 ActivationFn activation,
607 armnn::IConnectableLayer* prevLayer,
608 ConversionData& data);
609
610} // namespace armnn_driver
611
612///
613/// Utility templates
614///
615
616namespace armnn_driver
617{
618
619using namespace android::nn;
620
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100621template<typename HalPolicy,
622 typename HalOperand = typename HalPolicy::Operand,
623 typename HalOperation = typename HalPolicy::Operation,
624 typename HalModel = typename HalPolicy::Model>
625const HalOperand* GetInputOperand(const HalOperation& operation,
626 uint32_t inputIndex,
627 const HalModel& model,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100628 bool failOnIndexOutOfBounds = true)
arovir01b0717b52018-09-05 17:03:25 +0100629{
630 if (inputIndex >= operation.inputs.size())
631 {
saoste01b8471482018-10-10 09:44:51 +0100632 if (failOnIndexOutOfBounds)
633 {
634 Fail("%s: invalid input index: %i out of %i", __func__, inputIndex, operation.inputs.size());
635 }
arovir01b0717b52018-09-05 17:03:25 +0100636 return nullptr;
637 }
638
639 BOOST_ASSERT(operation.inputs[inputIndex] < model.operands.size()); // Model should have been validated beforehand
640 return &model.operands[operation.inputs[inputIndex]];
641}
642
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100643template<typename HalPolicy,
644 typename HalOperand = typename HalPolicy::Operand,
645 typename HalOperation = typename HalPolicy::Operation,
646 typename HalModel = typename HalPolicy::Model>
647const HalOperand* GetOutputOperand(const HalOperation& operation,
648 uint32_t outputIndex,
649 const HalModel& model)
arovir01b0717b52018-09-05 17:03:25 +0100650{
651 if (outputIndex >= operation.outputs.size())
652 {
653 Fail("%s: invalid output index: %i out of %i", __func__, outputIndex, operation.outputs.size());
654 return nullptr;
655 }
656
657 // Model should have been validated beforehand
658 BOOST_ASSERT(operation.outputs[outputIndex] < model.operands.size());
659
660 return &model.operands[operation.outputs[outputIndex]];
661}
662
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100663template<typename HalPolicy,
Pablo Tellofb45e2f2019-10-18 16:51:57 +0100664 typename HalOperand = typename HalPolicy::Operand,
665 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100666const void* GetOperandValueReadOnlyAddress(const HalOperand& operand,
Matthew Bentham912b3622019-05-03 15:49:14 +0100667 const HalModel& model,
668 const ConversionData& data,
Kevin Mayf29a2c52019-03-14 11:56:32 +0000669 bool optional = false)
arovir01b0717b52018-09-05 17:03:25 +0100670{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100671 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
arovir01b0717b52018-09-05 17:03:25 +0100672
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100673 const void* valueStart = nullptr;
arovir01b0717b52018-09-05 17:03:25 +0100674 switch (operand.lifetime)
675 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100676 case HalOperandLifeTime::CONSTANT_COPY:
arovir01b0717b52018-09-05 17:03:25 +0100677 {
678 // Constant found in model.operandValues
679 valueStart = &model.operandValues[operand.location.offset];
680 break;
681 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100682 case HalOperandLifeTime::CONSTANT_REFERENCE:
arovir01b0717b52018-09-05 17:03:25 +0100683 {
684 // Constant specified via a Memory object
685 valueStart = GetMemoryFromPool(operand.location, data.m_MemPools);
686 break;
687 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100688 case HalOperandLifeTime::NO_VALUE:
Kevin Mayf29a2c52019-03-14 11:56:32 +0000689 {
690 // An optional input tensor with no values is not an error so should not register as a fail
691 if (optional)
692 {
693 valueStart = nullptr;
694 break;
695 }
Matthew Bentham912b3622019-05-03 15:49:14 +0100696 [[fallthrough]];
Kevin Mayf29a2c52019-03-14 11:56:32 +0000697 }
arovir01b0717b52018-09-05 17:03:25 +0100698 default:
699 {
700 // Unsupported/invalid (e.g. can't get value of an input to the model)
701 Fail("%s: unsupported/invalid operand lifetime: %s",
702 __func__, toString(operand.lifetime).c_str());
703 valueStart = nullptr;
704 }
705 }
706
707 return valueStart;
708}
709
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100710template<typename HalPolicy,
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100711 typename HalOperation = typename HalPolicy::Operation,
712 typename HalModel = typename HalPolicy::Model,
713 typename HalOperandType = typename HalPolicy::OperandType>
714bool GetOperandType(const HalOperation& operation,
715 uint32_t inputIndex,
716 const HalModel& model,
717 HalOperandType& type)
718{
719 using HalOperand = typename HalPolicy::Operand;
720
721 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
722 if (!operand)
723 {
724 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
725 }
726
727 type = operand->type;
728 return true;
729}
730
731template<typename HalPolicy,
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000732 typename HalOperand = typename HalPolicy::Operand>
733bool IsOperandConstant(const HalOperand& operand)
734{
735 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
736
737 HalOperandLifeTime lifetime = operand.lifetime;
738
739 return lifetime == HalOperandLifeTime::CONSTANT_COPY ||
740 lifetime == HalOperandLifeTime::CONSTANT_REFERENCE ||
741 lifetime == HalOperandLifeTime::NO_VALUE;
742}
743
744template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100745 typename HalOperand = typename HalPolicy::Operand,
746 typename HalModel = typename HalPolicy::Model>
747ConstTensorPin ConvertOperandToConstTensorPin(const HalOperand& operand,
748 const HalModel& model,
749 const ConversionData& data,
750 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
751 const armnn::TensorShape* overrideTensorShape = nullptr,
752 bool optional = false)
753{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100754 if (!IsOperandTypeSupportedForTensors(operand.type))
755 {
756 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand.type).c_str());
757 return ConstTensorPin();
758 }
759
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +0000760 if (!optional && !IsOperandConstant<HalPolicy>(operand))
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100761 {
762 Fail("%s: invalid operand lifetime: %s", __func__, toString(operand.lifetime).c_str());
763 return ConstTensorPin();
764 }
765
766 const void* const valueStart = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data, optional);
767 if (!valueStart)
768 {
769 if (optional)
770 {
771 // optional tensor with no values is not really an error; return it as invalid, but marked as optional
772 return ConstTensorPin(true);
773 }
774 // mandatory tensor with no values
775 Fail("%s: failed to get operand address", __func__);
776 return ConstTensorPin();
777 }
778
779 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(operand);
Teresa Charlin02dce092019-11-11 17:06:23 +0000780 // Android datalayout might be different than armnn datalayout, e.g. the kernel for the depthwise convolution.
781 if (tensorInfo.HasPerAxisQuantization())
782 {
783 tensorInfo.SetQuantizationDim(dimensionMappings[tensorInfo.GetQuantizationDim().value()]);
784 }
785
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100786 if (overrideTensorShape != nullptr)
787 {
788 tensorInfo.SetShape(*overrideTensorShape);
789 }
790 return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings);
791}
792
793template<typename HalPolicy,
794 typename HalOperation = typename HalPolicy::Operation,
795 typename HalModel = typename HalPolicy::Model>
796ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operation,
797 uint32_t inputIndex,
798 const HalModel& model,
799 const ConversionData& data,
800 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
801 const armnn::TensorShape* overrideTensorShape = nullptr,
802 bool optional = false)
803{
804 using HalOperand = typename HalPolicy::Operand;
805
806 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
807 if (!operand)
808 {
809 Fail("%s: failed to get input operand: index=%u", __func__, inputIndex);
810 return ConstTensorPin();
811 }
812 return ConvertOperandToConstTensorPin<HalPolicy>(*operand,
813 model,
814 data,
815 dimensionMappings,
816 overrideTensorShape,
817 optional);
818}
819
820template<typename HalPolicy,
821 typename OutputType,
822 typename HalOperandType = typename HalPolicy::OperandType,
823 typename HalOperation = typename HalPolicy::Operation,
824 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100825bool GetInputScalar(const HalOperation& operation,
826 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100827 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100828 OutputType& outValue,
829 const HalModel& model,
830 const ConversionData& data)
831{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100832 using HalOperand = typename HalPolicy::Operand;
833
834 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +0100835 if (!operand)
836 {
837 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
838 }
839
840 if (operand->type != type)
841 {
842 return Fail("%s: unexpected operand type: %s (should be %s)",
843 __func__, toString(operand->type).c_str(), toString(type).c_str());
844 }
845
846 if (operand->location.length != sizeof(OutputType))
847 {
848 return Fail("%s: incorrect operand location length: %i (should be %i)",
849 __func__, operand->location.length, sizeof(OutputType));
850 }
851
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100852 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100853 if (!valueAddress)
854 {
855 return Fail("%s: failed to get address for operand", __func__);
856 }
857
858 outValue = *(static_cast<const OutputType*>(valueAddress));
859 return true;
860}
861
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100862template<typename HalPolicy,
863 typename HalOperation = typename HalPolicy::Operation,
864 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100865bool GetInputInt32(const HalOperation& operation,
866 uint32_t inputIndex,
867 int32_t& outValue,
868 const HalModel& model,
869 const ConversionData& data)
870{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100871 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::INT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100872}
873
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100874template<typename HalPolicy,
875 typename HalOperation = typename HalPolicy::Operation,
876 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100877bool GetInputFloat32(const HalOperation& operation,
878 uint32_t inputIndex,
879 float& outValue,
880 const HalModel& model,
881 const ConversionData& data)
882{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100883 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::FLOAT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100884}
885
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100886template<typename HalPolicy,
887 typename HalOperation = typename HalPolicy::Operation,
888 typename HalOperandType = typename HalPolicy::OperandType,
889 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100890bool GetInputActivationFunctionImpl(const HalOperation& operation,
891 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100892 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100893 ActivationFn& outActivationFunction,
894 const HalModel& model,
895 const ConversionData& data)
896{
Mike Kellyb5fdf382019-06-11 16:35:25 +0100897 if (type != HalOperandType::INT32 && type != HalOperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100898 {
899 return Fail("%s: unexpected operand type: %s (should be %s or %s)",
900 __func__,
901 toString(type).c_str(),
902 toString(OperandType::INT32).c_str(),
903 toString(OperandType::TENSOR_INT32).c_str());
904 }
905
906 int32_t activationFunctionAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100907 if (!GetInputScalar<HalPolicy>(operation, inputIndex, type, activationFunctionAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100908 {
909 return Fail("%s: failed to get activation input value", __func__);
910 }
911 outActivationFunction = static_cast<ActivationFn>(activationFunctionAsInt);
912 return true;
913}
914
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100915template<typename HalPolicy,
916 typename HalOperation = typename HalPolicy::Operation,
917 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100918bool GetInputActivationFunction(const HalOperation& operation,
919 uint32_t inputIndex,
920 ActivationFn& outActivationFunction,
921 const HalModel& model,
922 const ConversionData& data)
923{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100924 return GetInputActivationFunctionImpl<HalPolicy>(operation,
925 inputIndex,
926 HalPolicy::OperandType::INT32,
927 outActivationFunction,
928 model,
929 data);
arovir01b0717b52018-09-05 17:03:25 +0100930}
931
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100932template<typename HalPolicy,
933 typename HalOperation = typename HalPolicy::Operation,
934 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100935bool GetInputActivationFunctionFromTensor(const HalOperation& operation,
936 uint32_t inputIndex,
937 ActivationFn& outActivationFunction,
938 const HalModel& model,
939 const ConversionData& data)
940{
941 // This only accepts a 1-D tensor of size 1
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100942 return GetInputActivationFunctionImpl<HalPolicy>(operation,
943 inputIndex,
944 HalPolicy::OperandType::INT32,
945 outActivationFunction,
946 model,
947 data);
arovir01b0717b52018-09-05 17:03:25 +0100948}
949
950
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100951template<typename HalPolicy,
952 typename HalOperation = typename HalPolicy::Operation,
953 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100954bool GetOptionalInputActivation(const HalOperation& operation,
955 uint32_t inputIndex,
956 ActivationFn& activationFunction,
957 const HalModel& model,
958 const ConversionData& data)
959{
960 if (operation.inputs.size() <= inputIndex)
961 {
962 activationFunction = ActivationFn::kActivationNone;
963 }
964 else
965 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100966 if (!GetInputActivationFunction<HalPolicy>(operation, inputIndex, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100967 {
968 return Fail("%s: Operation has invalid inputs", __func__);
969 }
970 }
971 return true;
972}
973
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100974template<typename HalPolicy,
975 typename ConvolutionDescriptor,
976 typename HalOperation = typename HalPolicy::Operation,
977 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100978bool GetOptionalConvolutionDilationParams(const HalOperation& operation,
979 uint32_t dilationXIndex,
980 ConvolutionDescriptor& descriptor,
981 const HalModel& model,
982 const ConversionData& data)
983{
984 bool success = true;
985 if (operation.inputs.size() >= dilationXIndex + 2)
986 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100987 success &= GetInputScalar<HalPolicy>(operation,
988 dilationXIndex,
989 HalPolicy::OperandType::INT32,
990 descriptor.m_DilationX,
991 model,
992 data);
993 success &= GetInputScalar<HalPolicy>(operation,
994 dilationXIndex + 1,
995 HalPolicy::OperandType::INT32,
996 descriptor.m_DilationY,
997 model,
998 data);
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100999 }
1000
1001 return success;
1002}
1003
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001004template<typename HalPolicy,
1005 typename HalOperand = typename HalPolicy::Operand,
1006 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001007bool GetTensorInt32Values(const HalOperand& operand,
arovir01b0717b52018-09-05 17:03:25 +01001008 std::vector<int32_t>& outValues,
1009 const HalModel& model,
1010 const ConversionData& data)
1011{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001012 if (operand.type != HalPolicy::OperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +01001013 {
1014 return Fail("%s: invalid operand type: %s", __func__, toString(operand.type).c_str());
1015 }
1016
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001017 const void* startAddress = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001018 if (!startAddress)
1019 {
1020 return Fail("%s: failed to get operand address", __func__, operand.type);
1021 }
1022
1023 // Check number of bytes is sensible
1024 const uint32_t numBytes = operand.location.length;
1025 if (numBytes % sizeof(int32_t) != 0)
1026 {
1027 return Fail("%s: invalid number of bytes: %i, expected to be a multiple of %i",
1028 __func__, numBytes, sizeof(int32_t));
1029 }
1030
1031 outValues.resize(numBytes / sizeof(int32_t));
1032 memcpy(outValues.data(), startAddress, numBytes);
1033 return true;
1034}
1035
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001036template<typename HalPolicy,
1037 typename HalOperation = typename HalPolicy::Operation,
1038 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001039bool GetInputPaddingScheme(const HalOperation& operation,
1040 uint32_t inputIndex,
1041 PaddingScheme& outPaddingScheme,
1042 const HalModel& model,
1043 const ConversionData& data)
1044{
1045 int32_t paddingSchemeAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001046 if (!GetInputInt32<HalPolicy>(operation, inputIndex, paddingSchemeAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001047 {
1048 return Fail("%s: failed to get padding scheme input value", __func__);
1049 }
1050
1051 outPaddingScheme = static_cast<android::nn::PaddingScheme>(paddingSchemeAsInt);
1052 return true;
1053}
1054
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001055template<typename HalPolicy,
1056 typename HalOperation = typename HalPolicy::Operation,
1057 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001058LayerInputHandle ConvertToLayerInputHandle(const HalOperation& operation,
1059 uint32_t inputIndex,
1060 const HalModel& model,
1061 ConversionData& data)
1062{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001063 using HalOperand = typename HalPolicy::Operand;
Sadik Armagan44bcc022019-06-18 17:21:36 +01001064 using HalOperandType = typename HalPolicy::OperandType;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001065 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
1066
1067 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +01001068 if (!operand)
1069 {
1070 Fail("%s: failed to get input operand %i", __func__, inputIndex);
1071 return LayerInputHandle();
1072 }
1073
1074 if (!IsOperandTypeSupportedForTensors(operand->type))
1075 {
1076 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand->type).c_str());
1077 return LayerInputHandle();
1078 }
1079
Sadik Armagan44bcc022019-06-18 17:21:36 +01001080 try
arovir01b0717b52018-09-05 17:03:25 +01001081 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001082 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001083 if (IsDynamicTensor(operandTensorInfo))
1084 {
1085 Fail("%s: dynamic input tensors are not supported", __func__);
1086 return LayerInputHandle();
1087 }
arovir01b0717b52018-09-05 17:03:25 +01001088
Sadik Armagan44bcc022019-06-18 17:21:36 +01001089 switch (operand->lifetime)
arovir01b0717b52018-09-05 17:03:25 +01001090 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001091 case HalOperandLifeTime::MODEL_INPUT:
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001092 {
1093 // NOTE: We must check whether we can support the input tensor on at least one
1094 // of the provided backends; otherwise we cannot convert the operation
1095 bool isInputSupported = false;
1096 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1097 IsInputSupported,
1098 data.m_Backends,
1099 isInputSupported,
1100 operandTensorInfo);
1101
1102 if (!isInputSupported)
1103 {
1104 Fail("%s: unsupported input tensor", __func__);
1105 return LayerInputHandle();
1106 }
1107
1108 BOOST_FALLTHROUGH; // intentional fallthrough
1109 }
1110 case HalOperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001111 case HalOperandLifeTime::MODEL_OUTPUT:
arovir01b0717b52018-09-05 17:03:25 +01001112 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001113 // The tensor is either an operand internal to the model, or a model input.
1114 // It can be associated with an ArmNN output slot for an existing layer.
1115
1116 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
1117 const uint32_t operandIndex = operation.inputs[inputIndex];
1118 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
Sadik Armagan44bcc022019-06-18 17:21:36 +01001119 }
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001120 case HalOperandLifeTime::CONSTANT_COPY: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001121 case HalOperandLifeTime::CONSTANT_REFERENCE:
1122 {
1123 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
1124 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin<HalPolicy>(*operand, model, data);
1125 if (tensorPin.IsValid())
arovir01b0717b52018-09-05 17:03:25 +01001126 {
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001127 bool isSupported = false;
1128 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1129 IsConstantSupported,
1130 data.m_Backends,
1131 isSupported,
1132 tensorPin.GetConstTensor().GetInfo());
Mike Kelly28e3d9f2019-08-07 14:55:04 +01001133 if (!isSupported)
Sadik Armagan44bcc022019-06-18 17:21:36 +01001134 {
1135 return LayerInputHandle();
1136 }
1137
1138 armnn::IConnectableLayer* constantLayer =
1139 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
1140 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
1141 outputSlot.SetTensorInfo(tensorPin.GetConstTensor().GetInfo());
1142
1143 return LayerInputHandle(true, &outputSlot, operandTensorInfo);
1144 }
1145 else
1146 {
1147 Fail("%s: invalid operand tensor", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001148 return LayerInputHandle();
1149 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001150 break;
arovir01b0717b52018-09-05 17:03:25 +01001151 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001152 default:
arovir01b0717b52018-09-05 17:03:25 +01001153 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001154 // Unsupported lifetime for an input tensor
1155 Fail("%s: unsupported lifetime for input tensor: %s",
1156 __func__, toString(operand->lifetime).c_str());
arovir01b0717b52018-09-05 17:03:25 +01001157 return LayerInputHandle();
1158 }
arovir01b0717b52018-09-05 17:03:25 +01001159 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001160 }
1161 catch (UnsupportedOperand<HalOperandType>& e)
1162 {
1163 Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str());
1164 return LayerInputHandle();
arovir01b0717b52018-09-05 17:03:25 +01001165 }
1166}
1167
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001168template<typename HalPolicy,
1169 typename HalOperation = typename HalPolicy::Operation,
1170 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001171bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1172 uint32_t operationOutputIndex,
1173 armnn::IConnectableLayer& layer,
1174 uint32_t layerOutputIndex,
1175 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001176 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001177{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001178 using HalOperand = typename HalPolicy::Operand;
1179
1180 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001181 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
1182 {
1183 return false;
1184 }
1185
1186 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
1187
1188 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
1189 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
1190
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001191 outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand));
Mike Kellyb5fdf382019-06-11 16:35:25 +01001192
1193 return true;
1194}
1195
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001196template<typename HalPolicy,
1197 typename HalOperation = typename HalPolicy::Operation,
1198 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001199armnn::DataLayout OptionalDataLayout(const HalOperation& operation,
1200 uint32_t inputIndex,
1201 const HalModel& model,
1202 ConversionData& data)
1203{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001204 using HalOperand = typename HalPolicy::Operand;
1205
1206 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001207 if (!operand)
1208 {
1209 return armnn::DataLayout::NHWC;
1210 }
1211
1212 if (!IsBool(*operand))
1213 {
1214 return armnn::DataLayout::NHWC;
1215 }
1216
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001217 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001218 if (!valueAddress)
1219 {
1220 return armnn::DataLayout::NHWC;
1221 }
1222
1223 if (*(static_cast<const bool*>(valueAddress)))
1224 {
1225 return armnn::DataLayout::NCHW;
1226 }
1227 else
1228 {
1229 return armnn::DataLayout::NHWC;
1230 }
1231}
1232
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001233template<typename HalPolicy,
1234 typename HalOperation = typename HalPolicy::Operation,
1235 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001236bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1237 uint32_t outputIndex,
1238 armnn::IConnectableLayer& layer,
1239 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001240 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001241{
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001242 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1243 outputIndex,
1244 layer,
1245 outputIndex,
1246 model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001247 data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001248}
1249
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001250template<typename HalPolicy,
1251 typename HalOperation = typename HalPolicy::Operation,
1252 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001253bool ConvertToActivation(const HalOperation& operation,
1254 const char* operationName,
1255 const armnn::ActivationDescriptor& activationDesc,
1256 const HalModel& model,
1257 ConversionData& data)
1258{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001259 using HalOperand = typename HalPolicy::Operand;
1260
1261 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001262 if (!input.IsValid())
1263 {
1264 return Fail("%s: Input 0 is invalid", operationName);
1265 }
1266
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001267 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001268 if (!outputOperand)
1269 {
1270 return false;
1271 }
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001272
1273 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan2050c232019-07-23 16:59:58 +01001274 if (IsDynamicTensor(outInfo))
1275 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001276 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan2050c232019-07-23 16:59:58 +01001277 }
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001278
1279 bool isSupported = false;
1280 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1281 IsActivationSupported,
1282 data.m_Backends,
1283 isSupported,
1284 input.GetTensorInfo(),
1285 outInfo,
1286 activationDesc);
1287 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001288 {
1289 return false;
1290 }
1291
1292 armnn::IConnectableLayer* layer = data.m_Network->AddActivationLayer(activationDesc);
1293 BOOST_ASSERT(layer != nullptr);
1294 input.Connect(layer->GetInputSlot(0));
1295
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001296 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001297}
1298
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001299template<typename HalPolicy,
Sadik Armagan61113162019-07-25 09:09:40 +01001300 typename HalOperation = typename HalPolicy::Operation,
1301 typename HalModel = typename HalPolicy::Model>
1302bool ConvertReLu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1303{
1304 armnn::ActivationDescriptor desc;
1305 desc.m_Function = armnn::ActivationFunction::ReLu;
1306
1307 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1308}
1309
1310template<typename HalPolicy,
1311 typename HalOperation = typename HalPolicy::Operation,
1312 typename HalModel = typename HalPolicy::Model>
1313bool ConvertReLu1(const HalOperation& operation, const HalModel& model, ConversionData& data)
1314{
1315 armnn::ActivationDescriptor desc;
1316 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1317 desc.m_A = 1.0f;
1318 desc.m_B = -1.0f;
1319
1320 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1321}
1322
1323template<typename HalPolicy,
1324 typename HalOperation = typename HalPolicy::Operation,
1325 typename HalModel = typename HalPolicy::Model>
1326bool ConvertReLu6(const HalOperation& operation, const HalModel& model, ConversionData& data)
1327{
1328 armnn::ActivationDescriptor desc;
1329 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1330 desc.m_A = 6.0f;
1331
1332 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1333}
1334
1335template<typename HalPolicy,
1336 typename HalOperation = typename HalPolicy::Operation,
1337 typename HalModel = typename HalPolicy::Model>
1338bool ConvertTanH(const HalOperation& operation, const HalModel& model, ConversionData& data)
1339{
1340 armnn::ActivationDescriptor desc;
1341 desc.m_Function = armnn::ActivationFunction::TanH;
1342 desc.m_A = 1.0f; // android nn does not support tanH parameters
1343 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1344
1345 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1346}
1347
1348template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001349 typename HalOperation = typename HalPolicy::Operation,
1350 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001351bool ConvertPaddings(const HalOperation& operation,
1352 const HalModel& model,
1353 ConversionData& data,
1354 unsigned int rank,
1355 armnn::PadDescriptor& padDescriptor)
1356{
1357 using HalOperand = typename HalPolicy::Operand;
1358
1359 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
1360 if (!paddingsOperand)
1361 {
1362 return Fail("%s: Could not read paddings operand", __func__);
1363 }
1364
1365 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
1366 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2)
1367 {
1368 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
1369 }
1370
1371 std::vector<int32_t> paddings;
Mike Kellyeec836e2020-02-18 10:03:30 +00001372 if (!GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data))
1373 {
1374 return Fail("%s: Operation has invalid or unsupported paddings operand", __func__);
1375 }
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001376
1377 // add padding for each dimension of input tensor.
1378 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
1379 {
1380 int paddingBeforeInput = paddings[i];
1381 int paddingAfterInput = paddings[i + 1];
1382
1383 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
1384 {
1385 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
1386 }
1387
1388 padDescriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
1389 }
1390
1391 return true;
1392}
1393
1394template<typename HalPolicy,
1395 typename HalOperation = typename HalPolicy::Operation,
1396 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001397bool ConvertPooling2d(const HalOperation& operation,
1398 const char* operationName,
1399 armnn::PoolingAlgorithm poolType,
1400 const HalModel& model,
1401 ConversionData& data)
1402{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001403 using HalOperand = typename HalPolicy::Operand;
1404 using HalOperandType = typename HalPolicy::OperandType;
1405
1406 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001407 if (!input.IsValid())
1408 {
FinnWilliamsArm493e9b72019-11-25 16:02:07 +00001409 return Fail("%s: Operation Could not read input 0", operationName);
arovir01b0717b52018-09-05 17:03:25 +01001410 }
1411
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001412 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001413 if (!output)
1414 {
1415 return Fail("%s: Could not read output 0", __func__);
1416 }
1417
1418 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1419 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1420
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001421 if (IsDynamicTensor(outputInfo))
1422 {
1423 return Fail("%s: Dynamic output tensors are not supported", __func__);
1424 }
1425
arovir01b0717b52018-09-05 17:03:25 +01001426 armnn::Pooling2dDescriptor desc;
1427 desc.m_PoolType = poolType;
1428 desc.m_OutputShapeRounding = armnn::OutputShapeRounding::Floor;
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001429 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001430
1431 ActivationFn activation;
1432
Sadik Armagan15d63e22019-07-26 16:59:35 +01001433 auto inputSize = operation.inputs.size();
1434
1435 if (inputSize >= 10)
1436 {
1437 // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type)
1438 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1439 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1440 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1441 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1442 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1443 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1444 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1445 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1446 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
1447 {
1448 return Fail("%s: Operation has invalid inputs", operationName);
1449 }
1450
1451 if (Is12Operand(*output))
1452 {
1453 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
1454 }
1455 }
1456 else
arovir01b0717b52018-09-05 17:03:25 +01001457 {
1458 // one input, 6 parameters (padding, stridex, stridey, width, height, activation type)
1459 android::nn::PaddingScheme scheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001460 if (!GetInputPaddingScheme<HalPolicy>(operation, 1, scheme, model, data) ||
1461 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1462 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1463 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1464 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1465 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001466 {
1467 return Fail("%s: Operation has invalid inputs", operationName);
1468 }
1469
Sadik Armagan15d63e22019-07-26 16:59:35 +01001470 if (Is12Operand(*output))
arovir01b0717b52018-09-05 17:03:25 +01001471 {
Sadik Armagan15d63e22019-07-26 16:59:35 +01001472 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001473 }
FinnWilliamsArm493e9b72019-11-25 16:02:07 +00001474
1475 const armnnUtils::DataLayoutIndexed dataLayout(desc.m_DataLayout);
1476 const unsigned int inputWidth = inputInfo.GetShape()[dataLayout.GetWidthIndex()];
1477 const unsigned int inputHeight = inputInfo.GetShape()[dataLayout.GetHeightIndex()];
1478
1479 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, scheme);
1480 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, scheme);
arovir01b0717b52018-09-05 17:03:25 +01001481 }
1482
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001483 bool isSupported = false;
1484 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1485 IsPooling2dSupported,
1486 data.m_Backends,
1487 isSupported,
1488 inputInfo,
1489 outputInfo,
1490 desc);
1491 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001492 {
Éanna Ó Catháin3d1059c2018-10-11 15:53:04 +01001493 return false;
arovir01b0717b52018-09-05 17:03:25 +01001494 }
arovir01b0717b52018-09-05 17:03:25 +01001495
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001496 armnn::IConnectableLayer* pooling2dLayer = data.m_Network->AddPooling2dLayer(desc);
1497 if (!pooling2dLayer)
arovir01b0717b52018-09-05 17:03:25 +01001498 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001499 return Fail("%s: AddPooling2dLayer failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001500 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001501
1502 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, pooling2dLayer, data);
1503 if (!endLayer)
arovir01b0717b52018-09-05 17:03:25 +01001504 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001505 return Fail("%s: ProcessActivation failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001506 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001507
1508 input.Connect(pooling2dLayer->GetInputSlot(0));
1509
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001510 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001511}
1512
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001513template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001514 typename HalOperation = typename HalPolicy::Operation,
1515 typename HalModel = typename HalPolicy::Model>
1516bool ConvertAdd(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01001517{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001518 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01001519
1520 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1521 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1522
1523 if (!input0.IsValid() || !input1.IsValid())
1524 {
1525 return Fail("%s: Operation has invalid inputs", __func__);
1526 }
1527
1528 // The FuseActivation parameter is always the input index 2
1529 // and it should be optional
1530 ActivationFn activationFunction;
1531 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
1532 {
1533 return Fail("%s: Operation has invalid inputs", __func__);
1534 }
1535
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001536 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01001537 if (!outputOperand)
1538 {
1539 return false;
1540 }
1541
1542 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
1543 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
1544
1545 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
1546 if (IsDynamicTensor(outputInfo))
1547 {
1548 return Fail("%s: Dynamic output tensors are not supported", __func__);
1549 }
1550
1551 bool isSupported = false;
1552 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1553 IsAdditionSupported,
1554 data.m_Backends,
1555 isSupported,
1556 inputInfo0,
1557 inputInfo1,
1558 outputInfo);
1559 if (!isSupported)
1560 {
1561 return false;
1562 }
1563
1564 armnn::IConnectableLayer* const startLayer = data.m_Network->AddAdditionLayer();
1565 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
1566
1567 if (endLayer != nullptr)
1568 {
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00001569 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01001570 if (!isReshapeSupported)
1571 {
1572 return false;
1573 }
1574
Mike Kelly46272802019-08-14 17:00:48 +01001575 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1576 }
1577 else
1578 {
1579 return Fail("%s: ProcessActivation failed", __func__);
1580 }
1581}
1582
1583template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001584 typename HalOperation = typename HalPolicy::Operation,
1585 typename HalModel = typename HalPolicy::Model>
1586bool ConvertArgMinMax(const HalOperation& operation,
1587 const HalModel& model,
Francis Murtagh19fa0cc2019-11-19 12:06:47 +00001588 ConversionData& data,
1589 armnn::ArgMinMaxFunction argMinMaxFunction)
1590{
1591 ALOGV("argMinMaxFunction = %s", GetArgMinMaxFunctionAsCString(argMinMaxFunction));
1592
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001593 using HalOperand = typename HalPolicy::Operand;
Francis Murtagh19fa0cc2019-11-19 12:06:47 +00001594 using HalOperandType = typename HalPolicy::OperandType;
1595
1596 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1597
1598 if (!input0.IsValid())
1599 {
1600 return Fail("%s: Operation has invalid inputs", __func__);
1601 }
1602
1603 int32_t axis;
1604 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
1605 {
1606 return Fail("%s: Operation has invalid inputs. Failed to read axis.", __func__);
1607 }
1608
1609 const armnn::TensorInfo& inputInfo = input0.GetTensorInfo();
1610 int rank = static_cast<int>(inputInfo.GetNumDimensions());
1611
1612 if (((axis < -rank) && (axis < 0)) || ((axis >= rank) && (axis > 0)))
1613 {
1614 // Square bracket denotes inclusive n while parenthesis denotes exclusive n
1615 // E.g. Rank 4 tensor can have axis in range [-4, 3)
1616 // -1 == 3, -2 == 2, -3 == 1, -4 == 0
1617 return Fail("%s: Axis must be in range [-n, n)", __func__);
1618 }
1619
1620 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1621 if (!output)
1622 {
1623 return Fail("%s: Could not read output 0", __func__);
1624 }
1625
1626 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
1627
1628 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1629 if (IsDynamicTensor(outputInfo))
1630 {
1631 return Fail("%s: Dynamic output tensors are not supported", __func__);
1632 }
1633
1634 armnn::ArgMinMaxDescriptor descriptor;
1635 descriptor.m_Function = argMinMaxFunction;
1636 descriptor.m_Axis = axis;
1637
1638 bool isSupported = false;
1639 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1640 IsArgMinMaxSupported,
1641 data.m_Backends,
1642 isSupported,
1643 inputInfo0,
1644 outputInfo,
1645 descriptor);
1646 if (!isSupported)
1647 {
1648 return false;
1649 }
1650
1651 armnn::IConnectableLayer* layer = data.m_Network->AddArgMinMaxLayer(descriptor);
1652 assert(layer != nullptr);
1653
1654 input0.Connect(layer->GetInputSlot(0));
1655
1656 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1657}
1658
1659template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001660 typename HalOperation = typename HalPolicy::Operation,
1661 typename HalModel = typename HalPolicy::Model>
1662bool ConvertConcatenation(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kellyb8805202019-07-31 17:25:43 +01001663{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00001664 using HalOperand = typename HalPolicy::Operand;
Mike Kellyb8805202019-07-31 17:25:43 +01001665 using HalOperandType = typename HalPolicy::OperandType;
1666
1667 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
1668 if (operation.inputs.size() <= 1)
1669 {
1670 return Fail("%s: Operation has insufficient arguments", __func__);
1671 }
1672
1673 // Get inputs and outputs
1674 const std::size_t numInputTensors = operation.inputs.size() - 1;
1675
1676 int32_t concatDim;
1677 if (!GetInputScalar<HalPolicy>(operation, numInputTensors, HalOperandType::INT32, concatDim, model, data))
1678 {
1679 return Fail("%s: Operation has invalid inputs", __func__);
1680 }
1681
1682 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1683 if (!outputOperand)
1684 {
1685 return Fail("%s: Operation has no outputs", __func__);
1686 }
1687
1688
1689 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
1690 armnn::TensorShape outputShape = outputInfo.GetShape();
1691
1692 //
1693 // handle negative concat dims along the lines of tensorflow as described here:
1694 // https://www.tensorflow.org/api_docs/python/tf/concat
1695 // "negative axis refers to axis + rank(values)-th dimension"
1696 //
1697 if (concatDim < 0)
1698 {
1699 concatDim += outputShape.GetNumDimensions();
1700 }
1701
1702 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
1703 {
1704 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
1705 }
1706
1707 std::vector<LayerInputHandle> inputHandles;
1708 std::vector<armnn::TensorShape> inputShapes;
1709
1710 inputHandles.reserve(numInputTensors);
1711 inputShapes.reserve(numInputTensors);
1712
1713 bool inputsHaveBeenReshaped = false;
1714 unsigned int tensorDimensionsAdded = 0;
1715
1716 for (uint32_t i = 0; i < numInputTensors; ++i)
1717 {
1718 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, i, model);
1719 if (!operand)
1720 {
1721 return Fail("%s: Operation has invalid inputs", __func__);
1722 }
1723
Teresa Charlin3b959602019-10-31 17:05:47 +00001724 LayerInputHandle operandInputHandle = ConvertToLayerInputHandle<HalPolicy>(operation, i, model, data);
1725 if (!operandInputHandle.IsValid())
1726 {
1727 return Fail("%s: Operation has invalid inputs", __func__);
1728 }
Mike Kellyb8805202019-07-31 17:25:43 +01001729
Teresa Charlin3b959602019-10-31 17:05:47 +00001730 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
Mike Kellyb8805202019-07-31 17:25:43 +01001731 if (operandShape.GetNumDimensions() == 0)
1732 {
1733 return Fail("%s: Operands with rank 0 are not supported", __func__);
1734 }
1735
1736 if (RequiresReshape(operandShape))
1737 {
1738 inputsHaveBeenReshaped = true;
1739
1740 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
1741
1742 // Expand the tensor to three dimensions
1743 if (operandShape.GetNumDimensions() == 2)
1744 {
1745 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
1746 tensorDimensionsAdded = 1;
1747 }
1748 else
1749 {
1750 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
1751 tensorDimensionsAdded = 2;
1752 }
1753
Kevin Mayaed08ac2019-12-12 16:33:31 +00001754 armnn::ReshapeDescriptor reshapeDescriptor;
1755 reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
1756
1757 bool isSupported = false;
1758 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1759 IsReshapeSupported,
1760 data.m_Backends,
1761 isSupported,
1762 operandInputHandle.GetTensorInfo(),
1763 reshapeInfo,
1764 reshapeDescriptor);
1765 if (!isSupported)
1766 {
1767 return false;
1768 }
1769
Mike Kellyb8805202019-07-31 17:25:43 +01001770 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
1771 *data.m_Network,
1772 operandInputHandle,
1773 reshapeInfo
1774 );
1775
1776 // Point to the reshape operation rather then the input operation
1777 operandShape = reshapeInfo.GetShape();
1778 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
1779 }
1780
1781 inputShapes.emplace_back(operandShape);
1782 inputHandles.emplace_back(operandInputHandle);
1783
1784 if (!inputHandles.back().IsValid())
1785 {
1786 return Fail("%s: Operation has invalid inputs", __func__);
1787 }
1788 }
1789
1790 BOOST_ASSERT(inputShapes.size() == inputHandles.size());
1791
1792 if (inputsHaveBeenReshaped)
1793 {
1794 // Adjust the concatenation dimension by the amount of dimensions added (if any)
1795 concatDim += tensorDimensionsAdded;
1796
1797 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
1798 if (tensorDimensionsAdded == 1)
1799 {
1800 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
1801 }
1802 else if (tensorDimensionsAdded == 2)
1803 {
1804 outputShape = armnn::TensorShape({1, 1, outputShape[0]});
1805 }
1806 }
1807
1808 // Check if permutations is required and get the pair of permutations required for the concatenation.
1809 // Permutation is required when the concat dimension is 2 for a 4D tensor or 1 for a 3D tensor.
1810 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
1811 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
1812
1813 bool needPermute =
1814 CreateConcatPermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
1815
1816 if (needPermute)
1817 {
Mike Kelly4a956582020-02-28 10:32:09 +00001818 outputShape = armnnUtils::TransposeTensorShape(outputShape, permutationPair.first);
Mike Kellyb8805202019-07-31 17:25:43 +01001819 }
1820
1821 outputInfo.SetShape(outputShape);
1822
1823 // this is no-op for identity swizzles, otherwise it replaces both
1824 // the handles and shapes with the swizzled layer output handles and shapes
Kevin Mayaed08ac2019-12-12 16:33:31 +00001825 if (!CheckReshapeSupported(data, inputHandles, inputShapes, permutationPair.first, outputInfo))
1826 {
1827 return false;
1828 }
Mike Kellyb8805202019-07-31 17:25:43 +01001829
1830 // Create an armnn concat layer descriptor - this will also perform validation on the input shapes
1831 armnn::OriginsDescriptor concatDescriptor;
1832
1833 try
1834 {
1835 // The concat descriptor is always created across the only supported concat dimension
1836 // which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
1837 concatDescriptor =
1838 armnn::CreateDescriptorForConcatenation(inputShapes.begin(), inputShapes.end(), concatDim);
1839 }
Derek Lambertib9cb8442019-11-28 13:34:48 +00001840 catch (std::exception& error)
Mike Kellyb8805202019-07-31 17:25:43 +01001841 {
1842 return Fail("%s: Error preparing concat descriptor. %s", __func__, error.what());
1843 }
1844
1845 // Validate the output shape is correct given the input shapes based on the
1846 // only valid concat dimension which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
1847 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
1848 {
1849 return Fail("%s: Error validating the output shape for concat", __func__);
1850 }
1851
1852 std::vector<const armnn::TensorInfo*> inputTensorInfos;
1853 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
1854 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
1855
1856 bool isSupported = false;
1857 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1858 IsConcatSupported,
1859 data.m_Backends,
1860 isSupported,
1861 inputTensorInfos,
1862 outputInfo,
1863 concatDescriptor);
1864 if (!isSupported)
1865 {
1866 return false;
1867 }
1868
1869 armnn::IConnectableLayer* layer = data.m_Network->AddConcatLayer(concatDescriptor);
1870 assert(layer != nullptr);
1871 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1872
1873 // Connect inputs to the layer
1874 const int numInputSlots = layer->GetNumInputSlots();
1875 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
1876 for (int i = 0; i < numInputSlots; ++i)
1877 {
1878 // connect the input directly to the merge (concat) layer
1879 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
1880 }
1881
1882 if (needPermute)
1883 {
Mike Kelly4a956582020-02-28 10:32:09 +00001884 armnn::TransposeDescriptor transposeDesc;
1885 transposeDesc.m_DimMappings = permutationPair.second;
Kevin Mayaed08ac2019-12-12 16:33:31 +00001886
1887 bool isSupported = false;
1888 FORWARD_LAYER_SUPPORT_FUNC(__func__,
Mike Kelly4a956582020-02-28 10:32:09 +00001889 IsTransposeSupported,
Kevin Mayaed08ac2019-12-12 16:33:31 +00001890 data.m_Backends,
1891 isSupported,
1892 layer->GetOutputSlot(0).GetTensorInfo(),
1893 outputInfo,
Mike Kelly4a956582020-02-28 10:32:09 +00001894 transposeDesc);
Kevin Mayaed08ac2019-12-12 16:33:31 +00001895 if (!isSupported)
1896 {
1897 return false;
1898 }
Mike Kellyb8805202019-07-31 17:25:43 +01001899 // Add permutation layer and connect the output to it, the permutation becomes the output layer
Mike Kelly4a956582020-02-28 10:32:09 +00001900 armnn::IConnectableLayer& deswizzleLayer = AddTransposeLayer(*data.m_Network,
1901 layer->GetOutputSlot(0),
1902 permutationPair.second);
Mike Kellyb8805202019-07-31 17:25:43 +01001903 layer = &deswizzleLayer;
1904 }
1905
1906 if (inputsHaveBeenReshaped)
1907 {
1908 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
1909
1910 // Undo the reshape knowing the amount of dimensions added
1911 if (tensorDimensionsAdded == 1)
1912 {
1913 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
1914 afterConcatInfo.GetShape()[2] }));
1915 }
1916 else if (tensorDimensionsAdded == 2)
1917 {
1918 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2] }));
1919 }
1920
Kevin Mayaed08ac2019-12-12 16:33:31 +00001921 armnn::ReshapeDescriptor reshapeDescriptor;
1922 reshapeDescriptor.m_TargetShape = afterConcatInfo.GetShape();
1923
1924 bool isSupported = false;
1925 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1926 IsReshapeSupported,
1927 data.m_Backends,
1928 isSupported,
1929 layer->GetOutputSlot(0).GetTensorInfo(),
1930 afterConcatInfo,
1931 reshapeDescriptor);
1932 if (!isSupported)
1933 {
1934 return false;
1935 }
1936
Mike Kellyb8805202019-07-31 17:25:43 +01001937 layer = &AddReshapeLayer(
1938 *data.m_Network,
1939 layer->GetOutputSlot(0),
1940 afterConcatInfo
1941 );
1942 }
1943
1944 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1945}
1946
1947template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001948 typename HalOperation = typename HalPolicy::Operation,
1949 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001950bool ConvertConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1951{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001952 using HalOperand = typename HalPolicy::Operand;
1953 using HalOperandType = typename HalPolicy::OperandType;
1954
1955 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001956 if (!input.IsValid())
1957 {
1958 return Fail("%s: Operation has invalid inputs", __func__);
1959 }
1960
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001961 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001962 if (!output)
1963 {
1964 return Fail("%s: Could not read output 0", __func__);
1965 }
1966
1967 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001968 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001969
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001970 if (IsDynamicTensor(outputInfo))
1971 {
1972 return Fail("%s: Dynamic output tensors are not supported", __func__);
1973 }
1974
Mike Kellyb5fdf382019-06-11 16:35:25 +01001975 // ArmNN does not currently support non-fixed weights or bias
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001976 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1977 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001978
1979 if (!weightsPin.IsValid() || !biasPin.IsValid())
1980 {
1981 return Fail("%s: Operation has invalid inputs", __func__);
1982 }
1983
1984 armnn::ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001985 armnn::ConstTensor bias = biasPin.GetConstTensor();
Mike Kellyb5fdf382019-06-11 16:35:25 +01001986 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1987
1988 armnn::Convolution2dDescriptor desc;
1989 desc.m_DataLayout = armnn::DataLayout::NHWC;
1990 ActivationFn activation;
1991
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001992 if (operation.inputs.size() == 10)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001993 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001994 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1995 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1996 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1997 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1998 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1999 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002000 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002001 {
2002 return Fail("%s: Operation has invalid inputs", __func__);
2003 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01002004 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002005 else if (operation.inputs.size() == 7)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002006 {
2007 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002008 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
2009 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2010 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002011 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002012 {
2013 return Fail("%s: Operation has invalid inputs", __func__);
2014 }
2015
2016 const uint32_t kernelX = weights.GetShape()[2];
2017 const uint32_t kernelY = weights.GetShape()[1];
2018 const uint32_t inputX = inputInfo.GetShape()[2];
2019 const uint32_t inputY = inputInfo.GetShape()[1];
2020
2021 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
2022 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002023 }
2024 else
2025 {
2026 return Fail("%s: Unsupported number of operation inputs", __func__);
2027 }
2028
2029 desc.m_BiasEnabled = true;
2030 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
2031
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002032 bool isSupported = false;
2033 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2034 IsConvolution2dSupported,
2035 data.m_Backends,
2036 isSupported,
2037 inputInfo,
2038 outputInfo,
2039 desc,
2040 weights.GetInfo(),
2041 biases);
2042 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002043 {
2044 return false;
2045 }
2046
2047 armnn::IConnectableLayer* startLayer =
2048 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2049
2050 if (!startLayer)
2051 {
2052 return Fail("%s: AddConvolution2dLayer failed", __func__);
2053 }
2054
2055 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2056
2057 if (!endLayer)
2058 {
2059 return Fail("%s: ProcessActivation failed", __func__);
2060 }
2061
2062 input.Connect(startLayer->GetInputSlot(0));
2063
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002064 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002065}
2066
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002067template<typename HalPolicy,
2068 typename HalOperation = typename HalPolicy::Operation,
2069 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +01002070bool ConvertDepthToSpace(const HalOperation& operation, const HalModel& model, ConversionData& data)
2071{
2072 using HalOperand = typename HalPolicy::Operand;
2073 using HalOperandType = typename HalPolicy::OperandType;
2074
2075 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2076 if (!input.IsValid() )
2077 {
2078 return Fail("%s: Operation has invalid inputs", __func__);
2079 }
2080
2081 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2082 unsigned int rank = inputInfo.GetNumDimensions();
2083 if (rank != 4)
2084 {
2085 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2086 }
2087
2088 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2089 if (!output)
2090 {
2091 return Fail("%s: Could not read output 0", __func__);
2092 }
2093
2094 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2095 if (IsDynamicTensor(outputInfo))
2096 {
2097 return Fail("%s: Dynamic output tensors are not supported", __func__);
2098 }
2099
2100 armnn::DepthToSpaceDescriptor descriptor;
2101
2102 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, descriptor.m_BlockSize, model, data);
2103 if (descriptor.m_BlockSize <= 1)
2104 {
2105 return Fail("%s: Block size must be at least 1 in all dimensions");
2106 }
2107
2108 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
2109 if (Is12Operand(*output))
2110 {
2111 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2112 }
2113
2114 bool isSupported = false;
2115 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2116 IsDepthToSpaceSupported,
2117 data.m_Backends,
2118 isSupported,
2119 inputInfo,
2120 outputInfo,
2121 descriptor);
2122 if (!isSupported)
2123 {
2124 return false;
2125 }
2126
2127 armnn::IConnectableLayer* const layer = data.m_Network->AddDepthToSpaceLayer(descriptor);
2128 assert(layer != nullptr);
2129 input.Connect(layer->GetInputSlot(0));
2130
2131 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2132}
2133
2134template<typename HalPolicy,
2135 typename HalOperation = typename HalPolicy::Operation,
2136 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01002137bool ConvertDepthwiseConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2138{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002139 using HalOperand = typename HalPolicy::Operand;
2140 using HalOperandType = typename HalPolicy::OperandType;
2141
2142 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002143
2144 if (!input.IsValid())
2145 {
2146 return Fail("%s: Operation has invalid inputs", __func__);
2147 }
2148
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002149 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002150
2151 if (!output)
2152 {
2153 return Fail("%s: Could not read output 0", __func__);
2154 }
2155
2156 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002157 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002158
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002159 if (IsDynamicTensor(outputInfo))
2160 {
2161 return Fail("%s: Dynamic output tensors are not supported", __func__);
2162 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01002163
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002164 // ArmNN does not currently support non-fixed weights or bias
Mike Kellyb5fdf382019-06-11 16:35:25 +01002165 // 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 +01002166 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002167
2168 if (weightsOperand == nullptr)
2169 {
2170 return Fail("%s: Operand is invalid", __func__);
2171 }
2172 armnn::DepthwiseConvolution2dDescriptor desc;
2173 desc.m_DataLayout = armnn::DataLayout::NHWC;
2174
Mike Kellyb5fdf382019-06-11 16:35:25 +01002175 // Reinterpret weight data as [ H, W, I, M ]
2176 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
2177 weightsOperand->dimensions[2],
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002178 inputInfo.GetShape()[3],
2179 weightsOperand->dimensions[3] / inputInfo.GetShape()[3] });
Mike Kellyb5fdf382019-06-11 16:35:25 +01002180
2181 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
2182 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
2183
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002184 const ConstTensorPin weightsPin =
2185 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2186 1,
2187 model,
2188 data,
2189 HWIMToMIHW,
2190 &weightsShape);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002191
2192 // Bias is a 1D tensor
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002193 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002194
2195 if (!weightsPin.IsValid() || !biasPin.IsValid())
2196 {
2197 return Fail("%s: Operation has invalid inputs", __func__);
2198 }
2199
2200 armnn::ConstTensor weights = weightsPin.GetConstTensor();
2201 armnn::ConstTensor bias = biasPin.GetConstTensor();
2202 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2203
2204 ActivationFn activation;
2205
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002206 if (operation.inputs.size() == 11)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002207 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002208 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2209 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2210 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2211 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2212 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2213 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002214 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002215 {
2216 return Fail("%s: Operation has invalid inputs", __func__);
2217 }
2218 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002219 else if (operation.inputs.size() == 8)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002220 {
2221 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002222 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
2223 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2224 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002225 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002226 {
2227 return Fail("%s: Operation has invalid inputs", __func__);
2228 }
2229
2230 const uint32_t kernelX = weights.GetShape()[3];
2231 const uint32_t kernelY = weights.GetShape()[2];
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002232 const uint32_t inputX = inputInfo.GetShape()[2];
2233 const uint32_t inputY = inputInfo.GetShape()[1];
Mike Kellyb5fdf382019-06-11 16:35:25 +01002234
2235 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
2236 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
2237 }
2238 else
2239 {
2240 return Fail("%s: Unsupported number of operation inputs", __func__);
2241 }
2242
2243 desc.m_BiasEnabled = true;
2244 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
2245
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002246 bool isSupported = false;
2247 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2248 IsDepthwiseConvolutionSupported,
2249 data.m_Backends,
2250 isSupported,
2251 inputInfo,
2252 outputInfo,
2253 desc,
2254 weights.GetInfo(),
2255 biases);
2256 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002257 {
2258 return false;
2259 }
2260
2261 armnn::IConnectableLayer* startLayer =
2262 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2263 if (!startLayer)
2264 {
2265 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
2266 }
2267
2268 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2269 if (!endLayer)
2270 {
2271 return Fail("%s: ProcessActivation failed", __func__);
2272 }
2273
2274 input.Connect(startLayer->GetInputSlot(0));
2275
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002276 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01002277}
2278
Mike Kelly3c673942019-07-25 09:26:06 +01002279template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002280 typename HalOperation = typename HalPolicy::Operation,
2281 typename HalModel = typename HalPolicy::Model>
2282bool ConvertDequantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly3c673942019-07-25 09:26:06 +01002283{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002284 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002285
2286 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2287 if (!input.IsValid())
2288 {
2289 return Fail("%s: Operation has invalid input", __func__);
2290 }
2291
Sadik Armagan98c0f662019-11-21 15:54:36 +00002292 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2293 const armnn::Optional<unsigned int>& quantizationDim = inputInfo.GetQuantizationDim();
2294 if (quantizationDim.has_value() && quantizationDim.value() != 0)
2295 {
2296 return Fail("%s: Operation has quantization dimension different than 0", __func__);
2297 }
2298
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002299 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002300 if (!outputOperand)
2301 {
2302 return Fail("%s: Operation has invalid outputs", __func__);
2303 }
2304
2305 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2306 if (IsDynamicTensor(outputInfo))
2307 {
2308 return Fail("%s: Dynamic output tensors are not supported", __func__);
2309 }
2310
2311 bool isSupported = false;
2312 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2313 IsDequantizeSupported,
2314 data.m_Backends,
2315 isSupported,
Sadik Armagan98c0f662019-11-21 15:54:36 +00002316 inputInfo,
2317 outputInfo);
Mike Kelly46272802019-08-14 17:00:48 +01002318 if (!isSupported)
2319 {
2320 return false;
2321 }
2322
2323 armnn::IConnectableLayer* const layer = data.m_Network->AddDequantizeLayer();
2324 assert(layer != nullptr);
2325 input.Connect(layer->GetInputSlot(0));
2326
2327 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2328}
2329
2330template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002331 typename HalOperation = typename HalPolicy::Operation,
2332 typename HalModel = typename HalPolicy::Model>
2333bool ConvertDiv(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002334{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002335 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002336
2337 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2338 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
2339
2340 if (!input0.IsValid() || !input1.IsValid())
2341 {
2342 return Fail("%s: Operation has invalid inputs", __func__);
2343 }
2344
2345 // The FuseActivation parameter is always the input index 2
2346 // and it should be optional
2347 ActivationFn activationFunction;
2348 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2349 {
2350 return Fail("%s: Operation has invalid inputs", __func__);
2351 }
2352
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002353 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002354 if (!output)
2355 {
2356 return Fail("%s: Could not read output 0", __func__);
2357 }
2358
2359 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2360 if (IsDynamicTensor(outputInfo))
2361 {
2362 return Fail("%s: Dynamic output tensors are not supported", __func__);
2363 }
2364
2365 bool isSupported = false;
2366 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2367 IsDivisionSupported,
2368 data.m_Backends,
2369 isSupported,
2370 input0.GetTensorInfo(),
2371 input1.GetTensorInfo(),
2372 outputInfo);
2373 if (!isSupported)
2374 {
2375 return false;
2376 }
2377
2378 armnn::IConnectableLayer* const startLayer = data.m_Network->AddDivisionLayer();
2379 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2380
2381 if (endLayer)
2382 {
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00002383 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01002384 if (!isReshapeSupported)
2385 {
2386 return false;
2387 }
2388
Mike Kelly46272802019-08-14 17:00:48 +01002389 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2390 }
2391 return Fail("%s: ProcessActivation failed", __func__);
2392}
2393
2394template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002395 typename HalOperation = typename HalPolicy::Operation,
2396 typename HalModel = typename HalPolicy::Model>
2397bool ConvertFloor(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002398{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002399 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002400
2401 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2402 if (!input.IsValid())
2403 {
2404 return Fail("%s: Operation has invalid inputs", __func__);
2405 }
2406
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002407 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002408 if (!outputOperand)
2409 {
2410 return Fail("%s: Operation has invalid outputs", __func__);
2411 }
2412
2413 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2414 if (IsDynamicTensor(outputInfo))
2415 {
2416 return Fail("%s: Dynamic output tensors are not supported", __func__);
2417 }
2418
2419 bool isSupported = false;
2420 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2421 IsFloorSupported,
2422 data.m_Backends,
2423 isSupported,
2424 input.GetTensorInfo(),
2425 outputInfo);
2426 if (!isSupported)
2427 {
2428 return false;
2429 }
2430
2431 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
2432 assert(layer != nullptr);
2433 input.Connect(layer->GetInputSlot(0));
2434
2435 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2436}
2437
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002438inline bool IsQSymm8(const V1_0::Operand&)
2439{
2440 return false;
2441}
2442
2443#ifdef ARMNN_ANDROID_NN_V1_2
2444
2445inline bool IsQSymm8(const V1_2::Operand& operand)
2446{
2447 return operand.type == V1_2::OperandType::TENSOR_QUANT8_SYMM;
2448}
2449
2450#endif
2451
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002452enum class DequantizeStatus
2453{
2454 SUCCESS,
2455 NOT_REQUIRED,
2456 INVALID_OPERAND
2457};
2458
2459using DequantizeResult = std::tuple<std::unique_ptr<float[]>, size_t, armnn::TensorInfo, DequantizeStatus>;
2460
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002461template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002462 typename HalOperation = typename HalPolicy::Operation,
2463 typename HalModel = typename HalPolicy::Model>
2464DequantizeResult DequantizeIfRequired(size_t operand_index,
2465 const HalOperation& operation,
2466 const HalModel& model,
2467 const ConversionData& data)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002468{
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002469 using HalOperand = typename HalPolicy::Operand;
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002470
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002471 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, operand_index, model);
Sadik Armagand0811942019-11-18 17:11:21 +00002472 if (!weightsOperand)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002473 {
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002474 return { nullptr, 0, armnn::TensorInfo(), DequantizeStatus::INVALID_OPERAND };
Sadik Armagand0811942019-11-18 17:11:21 +00002475 }
2476
2477 if (IsOperandConstant<HalPolicy>(*weightsOperand))
2478 {
2479 // Weights are already constant
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002480 return { nullptr, 0, armnn::TensorInfo(), DequantizeStatus::NOT_REQUIRED };
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002481 }
2482
2483 const size_t weightsInputIndex = operation.inputs[operand_index];
2484
2485 // The weights are a non const tensor, this indicates they might be the output of a dequantize op.
2486 // Iterate over the nodes and find the previous operation which should be DEQUANTIZE
2487 for (uint32_t operationIdx = 0; operationIdx < model.operations.size(); ++operationIdx)
2488 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002489 // Search for the DEQUANTIZE op which has the operand with index equal to operandIndex
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002490 const auto& operationIt = model.operations[operationIdx];
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002491 if (operationIt.type != HalPolicy::OperationType::DEQUANTIZE)
2492 {
2493 continue;
2494 }
2495
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002496 size_t outOpIndex = weightsInputIndex + 1;
2497 for (size_t i = 0; outOpIndex != weightsInputIndex && i < operationIt.outputs.size(); ++i)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002498 {
2499 outOpIndex = operationIt.outputs[i];
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002500 }
2501
2502 if (outOpIndex != weightsInputIndex)
2503 {
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002504 continue;
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002505 }
2506
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002507 const HalOperand* operand = GetInputOperand<HalPolicy>(operationIt, 0, model);
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002508 BOOST_ASSERT(operand);
2509
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002510 if (!IsQSymm8(*operand))
2511 {
2512 // Only supporting dequantize from QSYMM8 to FLOAT
2513 break;
2514 }
2515
2516 // Allocate a new buffer for the dequantized data and manually dequantize
2517 const void* startValue = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
2518 if (!startValue)
2519 {
2520 // Failed to get the operand address
2521 break;
2522 }
2523
2524 const uint8_t* quantizedBuffer = reinterpret_cast<const uint8_t*>(startValue);
2525 size_t dequantizedBufferLength = operand->location.length;
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002526 const float quantizationScale = operand->scale;
2527
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002528 auto dequantizedBuffer = std::make_unique<float[]>(dequantizedBufferLength + 1);
2529 for (size_t i = 0; i < dequantizedBufferLength; ++i)
2530 {
2531 float* dstPtr = dequantizedBuffer.get();
2532 BOOST_ASSERT(dstPtr);
2533 *dstPtr++ = quantizedBuffer[i] * quantizationScale;
2534 }
2535
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002536 // Construct tensor info for dequantized ConstTensor
2537 armnn::TensorInfo tensorInfo(operand->dimensions.size(),
2538 operand->dimensions.data(),
2539 armnn::DataType::Float32);
2540
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002541 return { std::move(dequantizedBuffer), dequantizedBufferLength * sizeof(float),
2542 std::move(tensorInfo),
2543 DequantizeStatus::SUCCESS };
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002544 }
2545
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002546 return { nullptr, 0, armnn::TensorInfo() , DequantizeStatus::NOT_REQUIRED};
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002547}
2548
2549template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002550 typename HalOperation = typename HalPolicy::Operation,
2551 typename HalModel = typename HalPolicy::Model>
2552ConstTensorPin DequantizeAndMakeConstTensorPin(const HalOperation& operation,
2553 const HalModel& model,
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002554 const ConversionData& data,
2555 size_t operandIndex,
2556 bool optional = false)
2557{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002558 DequantizeResult dequantized = DequantizeIfRequired<HalPolicy>(operandIndex,operation, model, data);
2559
2560 DequantizeStatus status = std::get<3>(dequantized);
2561 switch (status)
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002562 {
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002563 case DequantizeStatus::INVALID_OPERAND:
2564 {
2565 // return invalid const tensor pin
2566 return ConstTensorPin();
2567 }
2568 case DequantizeStatus::NOT_REQUIRED:
2569 {
2570 return ConvertOperationInputToConstTensorPin<HalPolicy>(
2571 operation, operandIndex, model, data, g_DontPermute, nullptr, optional);
2572 }
2573 case DequantizeStatus::SUCCESS:
2574 default:
2575 {
2576 return ConstTensorPin(
2577 std::get<2>(dequantized), std::get<0>(dequantized).get(), std::get<1>(dequantized), g_DontPermute);
2578 }
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002579 }
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002580}
2581
2582
Mike Kelly46272802019-08-14 17:00:48 +01002583template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002584 typename HalOperation = typename HalPolicy::Operation,
2585 typename HalModel = typename HalPolicy::Model>
2586bool ConvertFullyConnected(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002587{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002588 using HalOperand = typename HalPolicy::Operand;
2589
Mike Kelly46272802019-08-14 17:00:48 +01002590 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2591 if (!input.IsValid())
2592 {
2593 return Fail("%s: Operation has invalid inputs", __func__);
2594 }
2595
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002596 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002597 if (!output)
2598 {
2599 return Fail("%s: Could not read output 0", __func__);
2600 }
2601
2602 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2603 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2604
2605 if (IsDynamicTensor(outputInfo))
2606 {
2607 return Fail("%s: Dynamic output tensors are not supported", __func__);
2608 }
2609
Aron Virginas-Tar65a1b1d2019-11-15 15:59:51 +00002610 ConstTensorPin weightsPin = DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1);
2611 ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data); // 1D
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002612
2613 if (!weightsPin.IsValid())
Mike Kelly46272802019-08-14 17:00:48 +01002614 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002615 return Fail("%s: Operation has invalid weights", __func__);
2616 }
2617
2618 if (!biasPin.IsValid())
2619 {
2620 return Fail("%s: Operation has invalid bias", __func__);
Mike Kelly46272802019-08-14 17:00:48 +01002621 }
2622
2623 armnn::ConstTensor weights = weightsPin.GetConstTensor();
2624 armnn::ConstTensor bias = biasPin.GetConstTensor();
2625 armnn::TensorInfo reshapedInfo = inputInfo;
2626
2627 try
2628 {
2629 reshapedInfo.SetShape(FlattenFullyConnectedInput(inputInfo.GetShape(), weights.GetInfo().GetShape()));
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002630 }
2631 catch (const std::exception& e)
2632 {
Mike Kelly46272802019-08-14 17:00:48 +01002633 return Fail("%s: %s", __func__, e.what());
2634 }
2635
2636 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
2637 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
2638
2639 ActivationFn activationFunction;
2640 if (!GetInputActivationFunction<HalPolicy>(operation, 3, activationFunction, model, data))
2641 {
2642 return Fail("%s: Operation has invalid inputs", __func__);
2643 }
2644
2645 armnn::FullyConnectedDescriptor desc;
2646 desc.m_TransposeWeightMatrix = true;
2647 desc.m_BiasEnabled = true;
2648
FinnWilliamsArm7b8d2e62020-01-08 14:57:47 +00002649 if (!VerifyFullyConnectedShapes(reshapedInfo.GetShape(),
2650 weights.GetInfo().GetShape(),
2651 outputInfo.GetShape(),
2652 desc.m_TransposeWeightMatrix))
2653 {
2654 return Fail("%s: Expected outputShape does not match actual outputShape", __func__);
2655 }
2656
Mike Kelly46272802019-08-14 17:00:48 +01002657 bool isSupported = false;
2658 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2659 IsFullyConnectedSupported,
2660 data.m_Backends,
2661 isSupported,
2662 reshapedInfo,
2663 outputInfo,
2664 weights.GetInfo(),
2665 bias.GetInfo(),
2666 desc);
2667 if (!isSupported)
2668 {
2669 return false;
2670 }
2671
2672 armnn::IConnectableLayer* startLayer =
2673 data.m_Network->AddFullyConnectedLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2674 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2675
2676 if (endLayer != nullptr)
2677 {
2678 if (inputInfo.GetNumDimensions() > 2U)
2679 {
2680 armnn::ReshapeDescriptor reshapeDescriptor;
2681 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
2682
2683 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
2684 assert(reshapeLayer != nullptr);
2685 input.Connect(reshapeLayer->GetInputSlot(0));
2686 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
2687 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
2688 }
2689 else
2690 {
2691 input.Connect(startLayer->GetInputSlot(0));
2692 }
2693
2694 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2695 }
2696 else
2697 {
2698 return Fail("%s: ProcessActivation failed", __func__);
2699 }
2700}
2701
2702template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002703 typename HalOperation = typename HalPolicy::Operation,
2704 typename HalModel = typename HalPolicy::Model>
2705bool ConvertL2Normalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002706{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002707 using HalOperand = typename HalPolicy::Operand;
2708
Mike Kelly999e2092019-08-15 10:46:46 +01002709 if (operation.inputs.size() != 1)
2710 {
2711 return Fail("%s: Optional inputs are not supported", __func__);
2712 }
2713
Mike Kelly46272802019-08-14 17:00:48 +01002714 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2715 if (!input.IsValid())
2716 {
2717 return Fail("%s: Operation has invalid inputs", __func__);
2718 }
2719
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002720 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002721 if (!output)
2722 {
2723 return Fail("%s: Could not read output 0", __func__);
2724 }
2725
2726 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2727 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2728
2729 if (IsDynamicTensor(outputInfo))
2730 {
2731 return Fail("%s: Dynamic output tensors are not supported", __func__);
2732 }
2733 if (outputInfo.GetNumDimensions() != 4u)
2734 {
2735 return Fail("%s: Tensor Rank other than 4 is not supported", __func__);
2736 }
2737
2738 armnn::L2NormalizationDescriptor desc;
2739 desc.m_DataLayout = armnn::DataLayout::NHWC;
2740
2741 bool isSupported = false;
2742 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2743 IsL2NormalizationSupported,
2744 data.m_Backends,
2745 isSupported,
2746 inputInfo,
2747 outputInfo,
2748 desc);
2749 if (!isSupported)
2750 {
2751 return false;
2752 }
2753
2754 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer(desc);
2755 assert(layer != nullptr);
2756 input.Connect(layer->GetInputSlot(0));
2757
2758 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2759}
2760
2761template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002762 typename HalOperation = typename HalPolicy::Operation,
2763 typename HalModel = typename HalPolicy::Model>
2764bool ConvertLocalResponseNormalization(const HalOperation& operation,
2765 const HalModel& model,
Mike Kelly46272802019-08-14 17:00:48 +01002766 ConversionData& data)
2767{
Mike Kelly999e2092019-08-15 10:46:46 +01002768 if (operation.inputs.size() != 5)
2769 {
2770 return Fail("%s: Optional inputs are not supported", __func__);
2771 }
2772
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002773 using HalOperand = typename HalPolicy::Operand;
2774 using HalOperandType = typename HalPolicy::OperandType;
Mike Kelly46272802019-08-14 17:00:48 +01002775
2776 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2777 if (!input.IsValid())
2778 {
2779 return Fail("%s: Operation has invalid inputs", __func__);
2780 }
2781
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002782 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002783 if (!output)
2784 {
2785 return Fail("%s: Could not read output 0", __func__);
2786 }
2787
2788 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2789 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2790
2791 if (IsDynamicTensor(outputInfo))
2792 {
2793 return Fail("%s: Dynamic output tensors are not supported", __func__);
2794 }
2795 if (outputInfo.GetNumDimensions() != 4u)
2796 {
2797 return Fail("%s: Tensor Rank other than 4 is not supported", __func__);
2798 }
2799
2800 armnn::NormalizationDescriptor descriptor;
2801 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
2802 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
2803 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
2804
2805 if (!input.IsValid() ||
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002806 !GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, descriptor.m_NormSize, model, data) ||
Mike Kelly46272802019-08-14 17:00:48 +01002807 !GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_K, model, data) ||
2808 !GetInputFloat32<HalPolicy>(operation, 3, descriptor.m_Alpha, model, data) ||
2809 !GetInputFloat32<HalPolicy>(operation, 4, descriptor.m_Beta, model, data))
2810 {
2811 return Fail("%s: Operation has invalid inputs", __func__);
2812 }
2813
2814 // ArmNN expects normSize to be the full size of the normalization
2815 // window rather than the radius as in AndroidNN.
2816 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
2817
2818 bool isSupported = false;
2819 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2820 IsNormalizationSupported,
2821 data.m_Backends,
2822 isSupported,
2823 inputInfo,
2824 outputInfo,
2825 descriptor);
2826 if (!isSupported)
2827 {
2828 return false;
2829 }
2830
2831
2832 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
2833 assert(layer != nullptr);
2834 input.Connect(layer->GetInputSlot(0));
2835
2836 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2837}
2838
2839template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002840 typename HalOperation = typename HalPolicy::Operation,
2841 typename HalModel = typename HalPolicy::Model>
2842bool ConvertLogistic(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002843{
Mike Kelly46272802019-08-14 17:00:48 +01002844 armnn::ActivationDescriptor desc;
2845 desc.m_Function = armnn::ActivationFunction::Sigmoid;
2846
2847 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
2848}
2849
2850template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002851 typename HalOperation = typename HalPolicy::Operation,
2852 typename HalModel = typename HalPolicy::Model>
2853bool ConvertMean(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002854{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002855 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002856
2857 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2858 if (!input.IsValid())
2859 {
2860 return Fail("%s: Operation has invalid inputs", __func__);
2861 }
2862
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002863 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002864 if (!output)
2865 {
2866 return Fail("%s: Could not read output 0", __func__);
2867 }
2868
2869 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2870 if (IsDynamicTensor(outputInfo))
2871 {
2872 return Fail("%s: Dynamic output tensors are not supported", __func__);
2873 }
2874
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002875 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Mike Kelly46272802019-08-14 17:00:48 +01002876 if (!axisOperand)
2877 {
2878 return Fail("%s: Could not read input 1", __func__);
2879 }
2880
2881 std::vector<int32_t> axis;
2882 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2883 {
2884 return Fail("%s: Input 1 has invalid values", __func__);
2885 }
2886
2887 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2888
2889 // Convert the axis to unsigned int and remove duplicates.
2890 unsigned int rank = inputInfo.GetNumDimensions();
2891 std::set<unsigned int> uniqueAxis;
2892 std::transform(axis.begin(), axis.end(),
2893 std::inserter(uniqueAxis, uniqueAxis.begin()),
2894 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2895
2896 // Get the "keep dims" flag.
2897 int32_t keepDims = 0;
2898 if (!GetInputInt32<HalPolicy>(operation, 2, keepDims, model, data))
2899 {
2900 return Fail("%s: Could not read input 2", __func__);
2901 }
2902
2903 armnn::MeanDescriptor descriptor;
2904 descriptor.m_Axis.assign(uniqueAxis.begin(), uniqueAxis.end());
2905 descriptor.m_KeepDims = keepDims > 0;
2906
2907 bool isSupported = false;
2908 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2909 IsMeanSupported,
2910 data.m_Backends,
2911 isSupported,
2912 inputInfo,
2913 outputInfo,
2914 descriptor);
2915 if (!isSupported)
2916 {
2917 return false;
2918 }
2919
2920 armnn::IConnectableLayer* const layer = data.m_Network->AddMeanLayer(descriptor);
2921 assert(layer != nullptr);
2922 input.Connect(layer->GetInputSlot(0));
2923
2924 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2925}
2926
2927template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002928 typename HalOperation = typename HalPolicy::Operation,
2929 typename HalModel = typename HalPolicy::Model>
2930bool ConvertMul(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01002931{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002932 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002933
2934 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2935 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
2936
2937 if (!input0.IsValid() || !input1.IsValid())
2938 {
2939 return Fail("%s: Operation has invalid inputs", __func__);
2940 }
2941
2942 // The FuseActivation parameter is always the input index 2
2943 // and it should be optional
2944 ActivationFn activationFunction;
2945 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2946 {
2947 return Fail("%s: Operation has invalid inputs", __func__);
2948 }
2949
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002950 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01002951
2952 if (outputOperand == nullptr)
2953 {
2954 return false;
2955 }
2956
2957 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2958 if (IsDynamicTensor(outputInfo))
2959 {
2960 return Fail("%s: Dynamic output tensors are not supported", __func__);
2961 }
2962
2963 bool isSupported = false;
2964 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2965 IsMultiplicationSupported,
2966 data.m_Backends,
2967 isSupported,
2968 input0.GetTensorInfo(),
2969 input1.GetTensorInfo(),
2970 outputInfo);
2971 if (!isSupported)
2972 {
2973 return false;
2974 }
2975
2976 armnn::IConnectableLayer* const startLayer = data.m_Network->AddMultiplicationLayer();
2977 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2978
2979 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
2980 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
2981
2982 if (endLayer != nullptr)
2983 {
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00002984 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01002985 if (!isReshapeSupported)
2986 {
2987 return false;
2988 }
2989
Mike Kelly46272802019-08-14 17:00:48 +01002990 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2991 }
2992 else
2993 {
2994 return Fail("%s: ProcessActivation failed", __func__);
2995 }
2996}
2997
2998template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00002999 typename HalOperation = typename HalPolicy::Operation,
3000 typename HalModel = typename HalPolicy::Model>
3001bool ConvertPad(HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003002{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003003 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003004
Mike Kelly3c673942019-07-25 09:26:06 +01003005 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3006 if (!input.IsValid())
3007 {
3008 return Fail("%s: Operation has invalid inputs", __func__);
3009 }
3010
3011 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3012 unsigned int rank = inputInfo.GetNumDimensions();
3013
3014 armnn::PadDescriptor descriptor;
3015 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
3016 {
3017 return Fail("%s: Could not convert paddings", __func__);
3018 }
3019
3020 // Before Android Q, the pad value for ANEURALNETWORKS_TENSOR_QUANT8_ASYMM was undefined. Since Android Q the pad
3021 // value must be "logical zero" we set it to be equal to the QuantizationOffset so effectively it ends up as
3022 // (QuantizationOffset - QuantizationOffset) * scale = 0.
Derek Lamberti1a38cda2020-01-10 17:28:20 +00003023 if (inputInfo.GetDataType() == armnn::DataType::QAsymmU8)
Mike Kelly3c673942019-07-25 09:26:06 +01003024 {
3025 descriptor.m_PadValue = inputInfo.GetQuantizationOffset();
3026 }
3027
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003028 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly3c673942019-07-25 09:26:06 +01003029 if (!output)
3030 {
3031 return Fail("%s: Could not read output", __func__);
3032 }
3033
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01003034 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kelly3c673942019-07-25 09:26:06 +01003035 if (IsDynamicTensor(outputInfo))
3036 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01003037 return Fail("%s: Dynamic output tensors are not supported", __func__);
Mike Kelly3c673942019-07-25 09:26:06 +01003038 }
3039
3040 bool isSupported = false;
3041 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3042 IsPadSupported,
3043 data.m_Backends,
3044 isSupported,
3045 inputInfo,
3046 outputInfo,
3047 descriptor);
3048 if (!isSupported)
3049 {
3050 return false;
3051 }
3052
3053 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
3054 assert(layer != nullptr);
3055 input.Connect(layer->GetInputSlot(0));
3056 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
3057
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01003058 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +01003059}
3060
Mike Kelly0a879362019-07-29 16:56:31 +01003061template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003062 typename HalOperation = typename HalPolicy::Operation,
3063 typename HalModel = typename HalPolicy::Model>
3064bool ConvertReshape(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003065{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003066 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003067
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003068 const HalOperand* inputOperand = GetInputOperand<HalPolicy>(operation, 0, model);
3069 const HalOperand* requestedShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3070 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003071
3072 if (inputOperand == nullptr
3073 || requestedShapeOperand == nullptr
3074 || outputOperand == nullptr)
3075 {
3076 return Fail("%s: Operation has invalid inputs", __func__);
3077 }
3078
3079 if (requestedShapeOperand->dimensions.size() != 1)
3080 {
3081 return Fail("%s: Input 1 expected to be one-dimensional (found %i dimensions)",
3082 __func__, requestedShapeOperand->dimensions.size());
3083 }
3084
3085 std::vector<int32_t> targetDimensions;
3086 if (!GetTensorInt32Values<HalPolicy>(*requestedShapeOperand, targetDimensions, model, data))
3087 {
3088 return Fail("%s: Could not read values of input 1", __func__);
3089 }
3090
3091 const Shape inputOperandShape = GetOperandShape(*inputOperand);
3092
3093 Shape requestedShape;
3094 // targetDimensions may contain special values (e.g. -1). reshapePrepare() is an AndroidNN provided utility
3095 // function that resolves these values into a fully specified tensor shape.
3096 if (!reshapePrepare(inputOperandShape, targetDimensions.data(), targetDimensions.size(), &requestedShape))
3097 {
3098 return Fail("%s: Failed to resolve the requested shape", __func__);
3099 }
3100
3101 const Shape outputOperandShape = GetOperandShape(*outputOperand);
3102 if (!SameShape(requestedShape, outputOperandShape))
3103 {
3104 return Fail("%s: Shape of output operand does not match resolved requested shape", __func__);
3105 }
3106
3107 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3108 if (!input.IsValid())
3109 {
3110 return Fail("%s: Could not read input 0", __func__);
3111 }
3112
3113 armnn::ReshapeDescriptor reshapeDescriptor;
3114 reshapeDescriptor.m_TargetShape = armnn::TensorShape(requestedShape.dimensions.size(),
3115 requestedShape.dimensions.data());
3116
3117 bool isSupported = false;
3118 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3119 IsReshapeSupported,
3120 data.m_Backends,
3121 isSupported,
3122 input.GetTensorInfo(),
Kevin Mayaed08ac2019-12-12 16:33:31 +00003123 GetTensorInfoForOperand(*outputOperand),
Mike Kelly46272802019-08-14 17:00:48 +01003124 reshapeDescriptor);
3125 if (!isSupported)
3126 {
3127 return false;
3128 }
3129
3130 armnn::IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
3131 assert(layer != nullptr);
3132 input.Connect(layer->GetInputSlot(0));
3133
3134 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3135}
3136
3137template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003138 typename HalOperation = typename HalPolicy::Operation,
3139 typename HalModel = typename HalPolicy::Model>
3140bool ConvertSub(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly0a879362019-07-29 16:56:31 +01003141{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003142 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003143
Mike Kelly0a879362019-07-29 16:56:31 +01003144 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3145 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
3146
3147 if (!input0.IsValid() || !input1.IsValid())
3148 {
3149 return Fail("%s: Operation has invalid inputs", __func__);
3150 }
3151
3152 // The FuseActivation parameter is always the input index 2
3153 // and it should be optional
3154 ActivationFn activationFunction;
3155 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
3156 {
3157 return Fail("%s: Operation has invalid inputs", __func__);
3158 }
3159
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003160 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly0a879362019-07-29 16:56:31 +01003161 if (!output)
3162 {
3163 return Fail("%s: Could not read output 0", __func__);
3164 }
3165
3166 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3167 if (IsDynamicTensor(outputInfo))
3168 {
3169 return Fail("%s: Dynamic output tensors are not supported", __func__);
3170 }
3171
3172 bool isSupported = false;
3173 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3174 IsSubtractionSupported,
3175 data.m_Backends,
3176 isSupported,
3177 input0.GetTensorInfo(),
3178 input1.GetTensorInfo(),
3179 outputInfo);
3180 if (!isSupported)
3181 {
3182 return false;
3183 }
3184
3185 armnn::IConnectableLayer* const startLayer = data.m_Network->AddSubtractionLayer();
3186 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
3187
3188 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
3189 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
3190
3191 if (endLayer)
3192 {
Derek Lamberti6fd4ceb2019-12-19 15:45:35 +00003193 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
Sadik Armagan64b19b52019-08-19 09:49:58 +01003194 if (!isReshapeSupported)
3195 {
3196 return false;
3197 }
Mike Kelly0a879362019-07-29 16:56:31 +01003198 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
3199 }
3200
3201 return Fail("%s: ProcessActivation failed", __func__);
3202}
3203
Finn Williams23b87b32019-07-30 11:44:05 +01003204template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003205 typename HalOperation = typename HalPolicy::Operation,
3206 typename HalModel = typename HalPolicy::Model>
3207bool ConvertSqueeze(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003208{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003209 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003210
3211 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3212 if (!input.IsValid())
3213 {
3214 return Fail("%s: Operation has invalid inputs", __func__);
3215 }
3216
3217 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3218 unsigned int rank = inputInfo.GetNumDimensions();
3219 if (rank > 4)
3220 {
3221 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3222 }
3223
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003224 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003225 if (!output)
3226 {
3227 return Fail("%s: Could not read output 0", __func__);
3228 }
3229
3230 if (IsDynamicTensor(GetTensorInfoForOperand(*output)))
3231 {
3232 return Fail("%s: Dynamic output tensors are not supported", __func__);
3233 }
3234
3235 // NOTE: Axis is an optional parameter to SQUEEZE, therefore we do not want to generate a failure
3236 // if the operand index is out of bounds.
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003237 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model, false);
Mike Kelly46272802019-08-14 17:00:48 +01003238
3239 const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
3240
3241 std::vector<int32_t> axis;
3242 if (!axisOperand)
3243 {
3244 axis.assign(dimensionSequence,
3245 dimensionSequence + rank);
3246 }
Mike Kellyeec836e2020-02-18 10:03:30 +00003247 else if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
Mike Kelly46272802019-08-14 17:00:48 +01003248 {
Mike Kellyeec836e2020-02-18 10:03:30 +00003249 return Fail("%s: Operation has an invalid or unsupported axis operand", __func__);
Mike Kelly46272802019-08-14 17:00:48 +01003250 }
3251
3252 std::vector<uint32_t> outputDims;
3253 for (unsigned int i = 0; i < rank; i++)
3254 {
3255 bool skipSqueeze = (std::find(axis.begin(), axis.end(), i) == axis.end());
3256 auto currentDimension = inputInfo.GetShape()[i];
3257 if (skipSqueeze || currentDimension != 1)
3258 {
3259 outputDims.push_back(currentDimension);
3260 }
3261 }
3262
3263 armnn::TensorShape outShape = armnn::TensorShape(outputDims.size(), outputDims.data());
3264
3265 armnn::TensorInfo outputInfo = inputInfo;
3266 outputInfo.SetShape(outShape);
3267
3268 armnn::ReshapeDescriptor reshapeDesc;
3269 reshapeDesc.m_TargetShape = outputInfo.GetShape();
3270
3271 bool isSupported = false;
3272 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3273 IsReshapeSupported,
3274 data.m_Backends,
3275 isSupported,
3276 inputInfo,
Kevin Mayaed08ac2019-12-12 16:33:31 +00003277 outputInfo,
Mike Kelly46272802019-08-14 17:00:48 +01003278 reshapeDesc);
3279 if (!isSupported)
3280 {
3281 return false;
3282 }
3283
3284 armnn::IConnectableLayer* const layer = data.m_Network->AddReshapeLayer(reshapeDesc);
3285 assert(layer != nullptr);
3286 input.Connect(layer->GetInputSlot(0));
3287
3288 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3289}
3290
3291template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003292 typename HalOperation = typename HalPolicy::Operation,
3293 typename HalModel = typename HalPolicy::Model>
3294bool ConvertStridedSlice(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003295{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003296 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003297
3298 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3299 if (!input.IsValid())
3300 {
3301 return Fail("%s: Operation has invalid inputs", __func__);
3302 }
3303
3304 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3305 unsigned int rank = inputInfo.GetNumDimensions();
3306 if (rank > 4)
3307 {
3308 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3309 }
3310
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003311 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003312 if (!output)
3313 {
3314 return Fail("%s: Could not read output 0", __func__);
3315 }
3316
3317 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3318 if (IsDynamicTensor(outputInfo))
3319 {
3320 return Fail("%s: Dynamic output tensors are not supported", __func__);
3321 }
3322
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003323 const HalOperand* beginOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3324 const HalOperand* endOperand = GetInputOperand<HalPolicy>(operation, 2, model);
3325 const HalOperand* stridesOperand = GetInputOperand<HalPolicy>(operation, 3, model);
Mike Kelly46272802019-08-14 17:00:48 +01003326
3327 std::vector<int32_t> beginValues;
3328 std::vector<int32_t> endValues;
3329 std::vector<int32_t> stridesValues;
3330
3331 // The length of the beginOperand, endOperand and stridesOperand must be of a rank(input)
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003332 auto ValidateInputOperands = [&] (const HalOperand& operand, std::vector<int32_t>& operandValues)
Mike Kelly46272802019-08-14 17:00:48 +01003333 {
3334 if (!GetTensorInt32Values<HalPolicy>(operand, operandValues, model, data))
3335 {
3336 return false;
3337 }
3338
3339 if (operandValues.size() != rank)
3340 {
3341 return false;
3342 }
3343
3344 return true;
3345 };
3346
3347 if (!ValidateInputOperands(*beginOperand, beginValues)
3348 || !ValidateInputOperands(*endOperand, endValues)
3349 || !ValidateInputOperands(*stridesOperand, stridesValues))
3350 {
3351 return Fail("%s: Operation has invalid input operand", __func__);
3352 }
3353
3354 // Stride cannot have value '0'
3355 if (std::any_of(stridesValues.cbegin(), stridesValues.cend(), [](int32_t i){ return i == 0; }))
3356 {
3357 return Fail("%s: Stride must be non-zero value.", __func__);
3358 }
3359
3360 armnn::StridedSliceDescriptor descriptor;
3361 descriptor.m_Begin.assign(beginValues.cbegin(), beginValues.cend());
3362 descriptor.m_End.assign(endValues.cbegin(), endValues.cend());
3363 descriptor.m_Stride.assign(stridesValues.cbegin(), stridesValues.cend());
3364 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
3365
3366 // Get the "begin_mask", "end_mask", and "shrink_axis_mask" flags
3367 if (!GetInputInt32<HalPolicy>(operation, 4, descriptor.m_BeginMask, model, data) ||
3368 !GetInputInt32<HalPolicy>(operation, 5, descriptor.m_EndMask, model, data) ||
3369 !GetInputInt32<HalPolicy>(operation, 6, descriptor.m_ShrinkAxisMask, model, data))
3370 {
3371 return Fail("%s: Operation has invalid inputs", __func__);
3372 }
3373
3374 bool isSupported = false;
3375 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3376 IsStridedSliceSupported,
3377 data.m_Backends,
3378 isSupported,
3379 inputInfo,
3380 outputInfo,
3381 descriptor);
3382 if (!isSupported)
3383 {
3384 return false;
3385 }
3386
3387 armnn::IConnectableLayer* const layer = data.m_Network->AddStridedSliceLayer(descriptor);
3388 assert(layer != nullptr);
3389 input.Connect(layer->GetInputSlot(0));
3390
3391 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3392}
3393
3394template<typename HalPolicy,
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003395 typename HalOperation = typename HalPolicy::Operation,
3396 typename HalModel = typename HalPolicy::Model>
3397bool ConvertTranspose(const HalOperation& operation, const HalModel& model, ConversionData& data)
Mike Kelly46272802019-08-14 17:00:48 +01003398{
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003399 using HalOperand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01003400
3401 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3402 if (!input.IsValid())
3403 {
3404 return Fail("%s: Operation has invalid inputs", __func__);
3405 }
3406
3407 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3408 unsigned int rank = inputInfo.GetNumDimensions();
3409 if (rank > 4)
3410 {
3411 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3412 }
3413
3414 // NOTE: Axis is an optional parameter to TRANSPOSE, therefore we do not want to generate a failure
3415 // if the operand index is out of bounds.
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003416 const HalOperand* permOperand = GetInputOperand<HalPolicy>(operation, 1, model, false);
Mike Kelly46272802019-08-14 17:00:48 +01003417
3418 std::vector<int32_t> perm(rank);
3419 if (!permOperand)
3420 {
3421 // NOTE: If perm is not given, it is set to (n-1...0), where n is the rank of the tensor
3422 for (unsigned int i = rank; i > 0; i--)
3423 {
3424 perm[rank - i] = boost::numeric_cast<int> (i - 1);
3425 }
3426 }
Mike Kellyeec836e2020-02-18 10:03:30 +00003427 else if (!GetTensorInt32Values<HalPolicy>(*permOperand, perm, model, data))
Mike Kelly46272802019-08-14 17:00:48 +01003428 {
Mike Kellyeec836e2020-02-18 10:03:30 +00003429 return Fail("%s: Operation has an invalid or unsupported permutation operand", __func__);
Mike Kelly46272802019-08-14 17:00:48 +01003430 }
3431
3432 std::vector<uint32_t> outputDims(perm.begin(), perm.begin() + rank);
3433
Mike Kelly4a956582020-02-28 10:32:09 +00003434 armnn::TransposeDescriptor transposeDesc;
3435 transposeDesc.m_DimMappings = armnn::PermutationVector(outputDims.data(), outputDims.size());
Mike Kelly46272802019-08-14 17:00:48 +01003436
Aron Virginas-Taraa5df2d2019-11-19 12:49:55 +00003437 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly46272802019-08-14 17:00:48 +01003438 if (!output)
3439 {
3440 return Fail("%s: Could not read output 0", __func__);
3441 }
3442
3443 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Matthew Bentham0182fd32019-12-06 09:45:13 +00003444 if (IsDynamicTensor(outputInfo))
3445 {
3446 return Fail("%s: Dynamic output tensors are not supported", __func__);
3447 }
3448
Mike Kelly46272802019-08-14 17:00:48 +01003449
3450 bool isSupported = false;
3451 FORWARD_LAYER_SUPPORT_FUNC(__func__,
Mike Kelly4a956582020-02-28 10:32:09 +00003452 IsTransposeSupported,
Mike Kelly46272802019-08-14 17:00:48 +01003453 data.m_Backends,
3454 isSupported,
3455 inputInfo,
3456 outputInfo,
Mike Kelly4a956582020-02-28 10:32:09 +00003457 transposeDesc);
Mike Kelly46272802019-08-14 17:00:48 +01003458 if (!isSupported)
3459 {
3460 return false;
3461 }
3462
Mike Kelly4a956582020-02-28 10:32:09 +00003463 armnn::IConnectableLayer* const layer = data.m_Network->AddTransposeLayer(transposeDesc);
Mike Kelly46272802019-08-14 17:00:48 +01003464 assert(layer != nullptr);
3465 input.Connect(layer->GetInputSlot(0));
3466
3467 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3468}
3469
3470template<typename HalPolicy,
Finn Williams23b87b32019-07-30 11:44:05 +01003471 typename HalOperation = typename HalPolicy::Operation,
Finn Williams0e4e4392019-07-31 10:56:27 +01003472 typename HalOperand = typename HalPolicy::Operand,
Finn Williams23b87b32019-07-30 11:44:05 +01003473 typename HalModel = typename HalPolicy::Model>
3474bool ConvertBatchToSpaceNd(const HalOperation& operation,
3475 const HalModel& model,
3476 ConversionData& data)
3477{
Finn Williams23b87b32019-07-30 11:44:05 +01003478
3479 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3480 if (!input.IsValid())
3481 {
3482 return Fail("%s: Operation has invalid inputs", __func__);
3483 }
3484
3485 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3486 if (!output)
3487 {
3488 return Fail("%s: Could not read output 0", __func__);
3489 }
3490
3491 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3492 if (IsDynamicTensor(outputInfo))
3493 {
3494 return Fail("%s: Dynamic output tensors are not supported", __func__);
3495 }
3496
3497 const HalOperand* blockOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3498 if (!blockOperand)
3499 {
3500 return Fail("%s: Could not read input 1", __func__);
3501 }
3502
3503 // Convert the block operand to int32
3504 std::vector<int32_t> block;
3505 if (!GetTensorInt32Values<HalPolicy>(*blockOperand, block, model, data))
3506 {
3507 return Fail("%s: Input 1 has invalid values", __func__);
3508 }
3509
3510 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3511
3512 unsigned int rank = inputInfo.GetNumDimensions();
3513 if (rank != 4)
3514 {
3515 Fail("%s: Only inputs with rank equal to 4 are supported", __func__);
3516 }
3517
3518 if (std::any_of(block.cbegin(), block.cend(), [](int32_t i){ return i < 1; }))
3519 {
3520 return Fail("%s: Block sizes for each spatial dimension of the input tensor must be"
3521 " greater than or equal to 1", __func__);
3522 }
3523
3524 armnn::BatchToSpaceNdDescriptor batchToSpaceNdDesc;
3525 batchToSpaceNdDesc.m_BlockShape.assign(block.cbegin(), block.cend());
3526 batchToSpaceNdDesc.m_DataLayout = armnn::DataLayout::NHWC;
3527
3528 if (Is12Operand(*output))
3529 {
Finn Williams0e4e4392019-07-31 10:56:27 +01003530 batchToSpaceNdDesc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +01003531 }
3532 // Setting crops to 0,0 0,0 as it is not supported in Android NN API
3533 batchToSpaceNdDesc.m_Crops = {{0, 0}, {0, 0}};
3534
3535 bool isSupported = false;
3536 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3537 IsBatchToSpaceNdSupported,
3538 data.m_Backends,
3539 isSupported,
3540 inputInfo,
3541 outputInfo,
3542 batchToSpaceNdDesc);
3543 if (!isSupported)
3544 {
3545 return false;
3546 }
3547
3548 armnn::IConnectableLayer* const layer = data.m_Network->AddBatchToSpaceNdLayer(batchToSpaceNdDesc);
3549 assert(layer != nullptr);
3550 input.Connect(layer->GetInputSlot(0));
3551
3552 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3553}
Mike Kelly0a879362019-07-29 16:56:31 +01003554
Finn Williamsd74c5052019-07-30 17:06:00 +01003555template<typename HalPolicy,
3556 typename HalOperation = typename HalPolicy::Operation,
3557 typename HalOperand = typename HalPolicy::Operand,
3558 typename HalModel = typename HalPolicy::Model>
3559bool ConvertSpaceToBatchNd(const HalOperation& operation, const HalModel& model, ConversionData& data)
3560{
3561 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3562 if (!input.IsValid())
3563 {
3564 return Fail("%s: Operation has invalid inputs", __func__);
3565 }
3566
3567 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3568 unsigned int rank = inputInfo.GetNumDimensions();
3569 unsigned int spatialDim = rank - 2;
3570
3571 if (rank != 4)
3572 {
3573 Fail("%s: Only inputs with rank 4 are supported", __func__);
3574 }
3575
3576 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3577 if (!output)
3578 {
3579 return Fail("%s: Could not read output 0", __func__);
3580 }
3581
3582 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3583 if (IsDynamicTensor(outputInfo))
3584 {
3585 return Fail("%s: Dynamic output tensors are not supported", __func__);
3586 }
3587
3588 const HalOperand* blockShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3589 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 2, model);
3590
3591 armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand(*blockShapeOperand);
3592 if (blockShapeOperandShape.GetNumDimensions() != 1 || blockShapeOperandShape.GetNumElements() != spatialDim)
3593 {
3594 return Fail("%s: Operation has invalid block shape operand: expected shape [%d]", __func__, spatialDim);
3595 }
3596
3597 std::vector<int32_t> blockShape;
Mike Kellyeec836e2020-02-18 10:03:30 +00003598 if (!GetTensorInt32Values<HalPolicy>(*blockShapeOperand, blockShape, model, data))
3599 {
3600 return Fail("%s: Operation has an invalid or unsupported block size operand", __func__);
3601 }
Finn Williamsd74c5052019-07-30 17:06:00 +01003602 if (std::any_of(blockShape.cbegin(), blockShape.cend(), [](int32_t i){ return i < 1; }))
3603 {
3604 return Fail("%s: Block shape must be at least 1 in all dimensions.", __func__);
3605 }
3606
3607 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
3608 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != 2 * spatialDim)
3609 {
3610 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, spatialDim);
3611 }
3612
3613 std::vector<std::pair<unsigned int, unsigned int>> paddingList;
3614 std::vector<int32_t> paddings;
Mike Kellyeec836e2020-02-18 10:03:30 +00003615 if (!GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data))
3616 {
3617 return Fail("%s: Operation has an invalid or unsupported paddings operand", __func__);
3618 }
Finn Williamsd74c5052019-07-30 17:06:00 +01003619 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
3620 {
3621 int paddingBeforeInput = paddings[i];
3622 int paddingAfterInput = paddings[i + 1];
3623 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
3624 {
3625 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
3626 }
3627
3628 paddingList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
3629 }
3630
3631 armnn::SpaceToBatchNdDescriptor descriptor;
3632 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
3633 descriptor.m_BlockShape.assign(blockShape.cbegin(), blockShape.cend());
3634 descriptor.m_PadList.assign(paddingList.cbegin(), paddingList.cend());
3635
3636 if (Is12Operand(*output))
3637 {
3638 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
3639 }
3640
3641 bool isSupported = false;
3642 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3643 IsSpaceToBatchNdSupported,
3644 data.m_Backends,
3645 isSupported,
3646 inputInfo,
3647 outputInfo,
3648 descriptor);
3649 if (!isSupported)
3650 {
3651 return false;
3652 }
3653
3654 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToBatchNdLayer(descriptor);
3655 assert(layer != nullptr);
3656 input.Connect(layer->GetInputSlot(0));
3657
3658 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3659}
3660
saoste01b8471482018-10-10 09:44:51 +01003661} // namespace armnn_driver