blob: 1975434a95f77e068a13df7596dc4b63e9931d71 [file] [log] [blame]
arovir01b0717b52018-09-05 17:03:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01008#include "Utils.hpp"
9
arovir01b0717b52018-09-05 17:03:25 +010010#include <armnn/ArmNN.hpp>
Ferran Balaguerd30093c2019-07-09 17:04:47 +010011#include <armnn/ILayerSupport.hpp>
12#include <armnn/BackendHelper.hpp>
arovir01b0717b52018-09-05 17:03:25 +010013
Mike Kellyb5fdf382019-06-11 16:35:25 +010014#include "armnn/src/armnnUtils/DataLayoutIndexed.hpp"
arovir01b0717b52018-09-05 17:03:25 +010015#include "armnn/src/armnnUtils/Permute.hpp"
arovir01b0717b52018-09-05 17:03:25 +010016
Mike Kelly46272802019-08-14 17:00:48 +010017#include "1.0/FullyConnected.hpp"
18
arovir01b0717b52018-09-05 17:03:25 +010019#include <ActivationFunctor.h>
20#include <CpuExecutor.h>
21#include <OperationsUtils.h>
22
23#include <boost/assert.hpp>
24#include <boost/core/ignore_unused.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
38struct ConversionData
39{
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010040 ConversionData(const std::vector<armnn::BackendId>& backends)
41 : m_Backends(backends)
42 , m_Network(nullptr, nullptr)
arovir01b0717b52018-09-05 17:03:25 +010043 {}
44
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010045 const std::vector<armnn::BackendId> m_Backends;
arovir01b0717b52018-09-05 17:03:25 +010046 armnn::INetworkPtr m_Network;
47 std::vector<armnn::IOutputSlot*> m_OutputSlotForOperand;
48 std::vector<android::nn::RunTimePoolInfo> m_MemPools;
49};
50
51class LayerInputHandle
52{
53public:
54 LayerInputHandle();
55 LayerInputHandle(bool valid, armnn::IOutputSlot* outputSlot, armnn::TensorInfo tensorInfo);
56
57 bool IsValid() const;
58
59 void Connect(armnn::IInputSlot& inputSlot);
60
61 const armnn::TensorInfo& GetTensorInfo() const;
62
63private:
64 armnn::IOutputSlot* m_OutputSlot;
65 bool m_Valid;
66 armnn::TensorInfo m_TensorInfo;
67};
68
69class ConstTensorPin
70{
71public:
72 // Creates an invalid tensor pin (can be used to signal errors)
73 // The optional flag can be set to indicate the tensor values were missing, but it was otherwise valid
74 ConstTensorPin(bool optional = false);
75
76 // @param tensorInfo TensorInfo associated with the tensor.
77 // @param valueStart Start address of tensor data. Belongs to one of the memory pools associated with
78 // the model being converted.
79 // @param numBytes Number of bytes for the tensor data.
80 ConstTensorPin(const armnn::TensorInfo& tensorInfo, const void* valueStart, uint32_t numBytes,
81 const armnn::PermutationVector& mappings);
82
83 ConstTensorPin(const ConstTensorPin& other) = delete;
84 ConstTensorPin(ConstTensorPin&& other) = default;
85
86 bool IsValid() const;
87 bool IsOptional() const;
88
89 const armnn::ConstTensor& GetConstTensor() const;
90 const armnn::ConstTensor* GetConstTensorPtr() const;
91
92private:
93 armnn::ConstTensor m_ConstTensor;
94
95 // Owned memory for swizzled tensor data, only required if the tensor needed
96 // swizzling. Otherwise, @ref m_ConstTensor will reference memory from one of
97 // the pools associated with the model being converted.
98 std::vector<uint8_t> m_SwizzledTensorData;
99
100 // optional flag to indicate that an invalid tensor pin is not an error, but the optional values were not given
101 bool m_Optional;
102};
103
104} // namespace armnn_driver
105
106///
107/// Utility functions
108///
109
110namespace
111{
112
113using namespace armnn_driver;
114using namespace android::nn;
115
116// Convenience function to log the reason for failing to convert a model.
117// @return Always returns false (so that it can be used by callers as a quick way to signal an error and return)
118template<class... Args>
119static bool Fail(const char* formatStr, Args&&... args)
120{
121 ALOGD(formatStr, std::forward<Args>(args)...);
122 return false;
123}
124
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100125// Convenience macro to call an Is*Supported function and log caller name together with reason for lack of support.
126// Called as: FORWARD_LAYER_SUPPORT_FUNC(__func__, Is*Supported, backends, a, b, c, d, e)
127#define FORWARD_LAYER_SUPPORT_FUNC(funcName, func, backends, supported, ...) \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100128try \
129{ \
130 for (auto&& backendId : backends) \
131 { \
132 auto layerSupportObject = armnn::GetILayerSupportByBackendId(backendId); \
133 if (layerSupportObject) \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100134 { \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100135 std::string reasonIfUnsupported; \
136 supported = \
137 layerSupportObject->func(__VA_ARGS__, armnn::Optional<std::string&>(reasonIfUnsupported)); \
138 if (supported) \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100139 { \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100140 break; \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100141 } \
142 else \
143 { \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100144 if (reasonIfUnsupported.size() > 0) \
145 { \
146 ALOGD("%s: not supported by armnn: %s", funcName, reasonIfUnsupported.c_str()); \
147 } \
148 else \
149 { \
150 ALOGD("%s: not supported by armnn", funcName); \
151 } \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100152 } \
153 } \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100154 else \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100155 { \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100156 ALOGD("%s: backend not registered: %s", funcName, backendId.Get().c_str()); \
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100157 } \
Teresa Charlin8f6429d2019-10-01 13:10:15 +0100158 } \
159 if (!supported) \
160 { \
161 ALOGD("%s: not supported by any specified backend", funcName); \
162 } \
163} \
164catch (const armnn::InvalidArgumentException &e) \
165{ \
166 throw armnn::InvalidArgumentException(e, "Failed to check layer support", CHECK_LOCATION()); \
167}
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100168
Mike Kellyb5fdf382019-06-11 16:35:25 +0100169template<typename Operand>
170armnn::TensorShape GetTensorShapeForOperand(const Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100171{
172 return armnn::TensorShape(operand.dimensions.size(), operand.dimensions.data());
173}
174
Matthew Bentham912b3622019-05-03 15:49:14 +0100175inline bool IsOperandTypeSupportedForTensors(V1_0::OperandType type)
arovir01b0717b52018-09-05 17:03:25 +0100176{
Matthew Bentham912b3622019-05-03 15:49:14 +0100177 return type == V1_0::OperandType::TENSOR_FLOAT32 ||
178 type == V1_0::OperandType::TENSOR_QUANT8_ASYMM ||
179 type == V1_0::OperandType::TENSOR_INT32;
arovir01b0717b52018-09-05 17:03:25 +0100180}
181
Mike Kellyb5fdf382019-06-11 16:35:25 +0100182#ifdef ARMNN_ANDROID_NN_V1_2
183
184inline bool IsOperandTypeSupportedForTensors(V1_2::OperandType type)
185{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000186 return type == V1_2::OperandType::BOOL ||
187 type == V1_2::OperandType::TENSOR_FLOAT16 ||
188 type == V1_2::OperandType::TENSOR_FLOAT32 ||
189 type == V1_2::OperandType::TENSOR_QUANT8_ASYMM ||
190 type == V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
191 type == V1_2::OperandType::TENSOR_QUANT16_SYMM ||
Mike Kellyb5fdf382019-06-11 16:35:25 +0100192 type == V1_2::OperandType::TENSOR_INT32;
193}
194
195#endif
196
197inline bool IsBool(V1_0::Operand)
198{
199 return false;
200}
201
Sadik Armagan61113162019-07-25 09:09:40 +0100202inline bool Is12Operand(V1_0::Operand)
203{
204 return false;
205}
206
Mike Kellyb5fdf382019-06-11 16:35:25 +0100207#ifdef ARMNN_ANDROID_NN_V1_2
208
209inline bool IsBool(V1_2::Operand operand)
210{
211 return operand.type == V1_2::OperandType::BOOL;
212}
213
Sadik Armagan61113162019-07-25 09:09:40 +0100214/// Checks if a operand is 1_2 Operand
215inline bool Is12Operand(V1_2::Operand)
216{
217 return true;
218}
219
Mike Kellyb5fdf382019-06-11 16:35:25 +0100220#endif
221
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100222template<typename LayerHandleType>
223armnn::IConnectableLayer& AddReshapeLayer(armnn::INetwork& network, LayerHandleType& inputLayer,
224 armnn::TensorInfo reshapeInfo)
225{
226 armnn::ReshapeDescriptor reshapeDescriptor;
227 reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
228
229 armnn::IConnectableLayer* reshapeLayer = network.AddReshapeLayer(reshapeDescriptor);
230 BOOST_ASSERT(reshapeLayer != nullptr);
231
232 // Attach the input layer to the reshape layer
233 inputLayer.Connect(reshapeLayer->GetInputSlot(0));
234 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapeInfo);
235
236 return *reshapeLayer;
237}
238
Sadik Armagan64b19b52019-08-19 09:49:58 +0100239bool BroadcastTensor(LayerInputHandle& input0, LayerInputHandle& input1,
240 armnn::IConnectableLayer* startLayer, ConversionData& data)
arovir01b0717b52018-09-05 17:03:25 +0100241{
242 BOOST_ASSERT(startLayer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +0100243
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100244 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
245 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
246
247 unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
248 unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
249
250 if (inputDimensions0 == inputDimensions1)
arovir01b0717b52018-09-05 17:03:25 +0100251 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100252 // The inputs have the same number of dimensions, simply connect them to the given layer as they are
253 input0.Connect(startLayer->GetInputSlot(0));
254 input1.Connect(startLayer->GetInputSlot(1));
255
Sadik Armagan64b19b52019-08-19 09:49:58 +0100256 return true;
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100257 }
258
259 // Since the number of dimensions do not match then we need to add degenerate dimensions
260 // to the "smaller" tensor using a reshape, while keeping the order of the inputs.
261
262 unsigned int maxInputDimensions = std::max(inputDimensions0, inputDimensions1);
263 unsigned int sizeDifference = std::abs(boost::numeric_cast<int>(inputDimensions0) -
264 boost::numeric_cast<int>(inputDimensions1));
265
266 bool input0IsSmaller = inputDimensions0 < inputDimensions1;
267 LayerInputHandle& smallInputHandle = input0IsSmaller ? input0 : input1;
268 const armnn::TensorInfo& smallInfo = smallInputHandle.GetTensorInfo();
269
270 const armnn::TensorShape& smallShape = smallInfo.GetShape();
271 std::vector<unsigned int> reshapedDimensions(maxInputDimensions, 1);
272 for (unsigned int i = sizeDifference; i < maxInputDimensions; i++)
273 {
274 reshapedDimensions[i] = smallShape[i - sizeDifference];
275 }
276
277 armnn::TensorInfo reshapedInfo = smallInfo;
278 reshapedInfo.SetShape(armnn::TensorShape{ boost::numeric_cast<unsigned int>(reshapedDimensions.size()),
279 reshapedDimensions.data() });
Sadik Armagan64b19b52019-08-19 09:49:58 +0100280
281 // RehsapeDescriptor that is ignored in the IsReshapeSupported function
282 armnn::ReshapeDescriptor reshapeDescriptor;
283
284 bool isSupported = false;
285 FORWARD_LAYER_SUPPORT_FUNC(__func__,
286 IsReshapeSupported,
287 data.m_Backends,
288 isSupported,
289 reshapedInfo,
290 reshapeDescriptor);
291 if (!isSupported)
292 {
293 return false;
294 }
295
296 BOOST_ASSERT(data.m_Network != nullptr);
297 armnn::IConnectableLayer& reshapeLayer = AddReshapeLayer(*data.m_Network, smallInputHandle, reshapedInfo);
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100298
299 if (input0IsSmaller)
300 {
301 // Input0 is the "smaller" tensor, connect the reshape layer as follows:
302 //
303 // Input0 Input1
arovir01b0717b52018-09-05 17:03:25 +0100304 // | |
305 // Reshape |
306 // \ /
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100307 // StartLayer
arovir01b0717b52018-09-05 17:03:25 +0100308
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100309 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
310 input1.Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100311 }
312 else
313 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100314 // Input1 is the "smaller" tensor, connect the reshape layer as follows:
315 //
316 // Input0 Input1
317 // | |
318 // | Reshape
319 // \ /
320 // StartLayer
321
arovir01b0717b52018-09-05 17:03:25 +0100322 input0.Connect(startLayer->GetInputSlot(0));
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100323 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100324 }
Sadik Armagan64b19b52019-08-19 09:49:58 +0100325
326 return true;
arovir01b0717b52018-09-05 17:03:25 +0100327}
328
329void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t& outPadHead, uint32_t& outPadTail,
330 android::nn::PaddingScheme scheme)
331{
332 int32_t padHead;
333 int32_t padTail;
334 calculateExplicitPadding(input, stride, kernel, scheme, &padHead, &padTail);
335 outPadHead = boost::numeric_cast<uint32_t>(padHead);
336 outPadTail = boost::numeric_cast<uint32_t>(padTail);
337}
338
Mike Kelly86b36d42019-07-12 16:39:33 +0100339#ifdef ARMNN_ANDROID_NN_V1_2
340
341void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t dilation, uint32_t& outPadHead,
342 uint32_t& outPadTail, android::nn::PaddingScheme scheme)
343{
344 int32_t padHead;
345 int32_t padTail;
346 calculateExplicitPadding(input, stride, dilation, kernel, scheme, &padHead, &padTail);
347 outPadHead = boost::numeric_cast<uint32_t>(padHead);
348 outPadTail = boost::numeric_cast<uint32_t>(padTail);
349}
350
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +0100351void CalcPaddingTransposeConv(uint32_t output, uint32_t kernel, uint32_t stride, int32_t& outPadHead,
352 int32_t& outPadTail, android::nn::PaddingScheme scheme)
353{
354 calculateExplicitPaddingTransposeConv(output, stride, kernel, scheme, &outPadHead, &outPadTail);
355}
356
Mike Kelly86b36d42019-07-12 16:39:33 +0100357#endif
358
Matthew Bentham912b3622019-05-03 15:49:14 +0100359Shape GetOperandShape(const V1_0::Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100360{
361 Shape shape;
Matthew Bentham912b3622019-05-03 15:49:14 +0100362 shape.type = OperandType(operand.type);
arovir01b0717b52018-09-05 17:03:25 +0100363 shape.dimensions = operand.dimensions;
364 shape.scale = operand.scale;
365 shape.offset = operand.zeroPoint;
366 return shape;
367}
368
Mike Kelly46272802019-08-14 17:00:48 +0100369#ifdef ARMNN_ANDROID_NN_V1_2
370
371Shape GetOperandShape(const V1_2::Operand& operand)
372{
373 Shape shape;
374 shape.type = OperandType(operand.type);
375 shape.dimensions = operand.dimensions;
376 shape.scale = operand.scale;
377 shape.offset = operand.zeroPoint;
378 return shape;
379}
380
381#endif
382
arovir01b0717b52018-09-05 17:03:25 +0100383// ArmNN requires the bias scale to be equal to the product of the weight and input scales, which is also
384// 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 +0100385// we accept some tolerance. We don't want ArmNN itself to accept these inconsistencies as it is up to the
386// user (us, in this case) to ensure they match.
arovir01b0717b52018-09-05 17:03:25 +0100387void SanitizeBiasQuantizationScale(armnn::TensorInfo& biasInfo,
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000388 const armnn::TensorInfo& weightInfo,
389 const armnn::TensorInfo& inputInfo)
arovir01b0717b52018-09-05 17:03:25 +0100390{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000391 if (weightInfo.HasPerAxisQuantization())
arovir01b0717b52018-09-05 17:03:25 +0100392 {
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000393 // NOTE: Bias scale is always set to 0 for per-axis quantization and
394 // it needs to be calculated: scale[i] = input_scale * weight_scale[i]
395 auto UpdateBiasScaleValue = [&inputInfo](float biasScale) -> float
arovir01b0717b52018-09-05 17:03:25 +0100396 {
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000397 return biasScale * inputInfo.GetQuantizationScale();
398 };
399
400 std::vector<float> biasScales(weightInfo.GetQuantizationScales());
401 std::transform(biasScales.begin(), biasScales.end(), biasScales.begin(), UpdateBiasScaleValue);
402
403 biasInfo.SetQuantizationScales(biasScales);
404 biasInfo.SetQuantizationDim(weightInfo.GetQuantizationDim());
405
406 ALOGV("Bias quantization params have been updated for per-axis quantization");
407 }
408 else
409 {
410 const float expectedBiasScale = weightInfo.GetQuantizationScale() * inputInfo.GetQuantizationScale();
411 if (biasInfo.GetQuantizationScale() != expectedBiasScale)
412 {
413 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
414 if (comparer(biasInfo.GetQuantizationScale(), expectedBiasScale))
415 {
416 ALOGW("Bias quantization scale has been modified to match input * weights");
417 biasInfo.SetQuantizationScale(expectedBiasScale);
418 }
arovir01b0717b52018-09-05 17:03:25 +0100419 }
420 }
421}
422
423// 4D Tensor Permutations
424const armnn::PermutationVector IdentityPermutation4D({ 0U, 1U, 2U, 3U });
425const armnn::PermutationVector NHWCToArmNN({ 0U, 2U, 3U, 1U });
426const armnn::PermutationVector ArmNNToNHWC({ 0U, 3U, 1U, 2U });
427const armnn::PermutationVector SwapDim1And2({ 0U, 2U, 1U, 3U });
428
429// 3D Permutation Vectors
430const armnn::PermutationVector IdentityPermutation3D({ 0U, 1U, 2U });
431const armnn::PermutationVector RotateTensorLeft({ 2U, 0U, 1U });
432const armnn::PermutationVector RotateTensorRight({ 1U, 2U, 0U });
433
434template<typename OSlot>
435armnn::IConnectableLayer& AddPermuteLayer(armnn::INetwork& network, OSlot& input,
436 const armnn::PermutationVector& mappings)
437{
438 // Add swizzle layer
439 armnn::IConnectableLayer* const layer = network.AddPermuteLayer(mappings);
440
441 BOOST_ASSERT(layer != nullptr);
442
443 // Connect input to swizzle layer
444 input.Connect(layer->GetInputSlot(0));
445
446 // Setup swizzled output
447 const armnn::TensorInfo outInfo = armnnUtils::Permuted(input.GetTensorInfo(), mappings);
448 layer->GetOutputSlot(0).SetTensorInfo(outInfo);
449
450 return *layer;
451}
452
453void SwizzleIn(armnn::INetwork& network, LayerInputHandle& input, armnn::IConnectableLayer& layer, unsigned int index)
454{
455 // Add swizzle layer
456 armnn::IConnectableLayer& swizzleLayer = AddPermuteLayer(network, input, NHWCToArmNN);
457 // Connect swizzled input to layer
458 swizzleLayer.GetOutputSlot(0).Connect(layer.GetInputSlot(index));
459}
460
461armnn::IConnectableLayer& DeswizzleOut(armnn::INetwork& network, armnn::IConnectableLayer& layer, unsigned int index)
462{
463 // Add deswizzle layer
464 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(network, layer.GetOutputSlot(index), ArmNNToNHWC);
465 return deswizzleLayer;
466}
467
468// only suitable for input/output slot index 0, for other slots, use SwizzleIn and DeswizzleOut directly
469armnn::IConnectableLayer& SwizzleInDeswizzleOut(armnn::INetwork& network,
470 LayerInputHandle& input,
471 armnn::IConnectableLayer& firstLayer,
472 armnn::IConnectableLayer& lastLayer)
473{
474 SwizzleIn(network, input, firstLayer, 0);
475 return DeswizzleOut(network, lastLayer, 0);
476}
477
478// only suitable for input/output slot index 0, for other slots, use SwizzleIn and DeswizzleOut directly
479armnn::IConnectableLayer& SwizzleInDeswizzleOut(armnn::INetwork& network, LayerInputHandle& input,
480 armnn::IConnectableLayer& layer)
481{
482 return SwizzleInDeswizzleOut(network, input, layer, layer);
483}
484
485bool ValidateConcatOutputShape(const std::vector<armnn::TensorShape> & inputShapes,
486 const armnn::TensorShape & outputShape,
487 uint32_t concatDim)
488{
489 // Validate the output shape is correct given the input shapes (which have just been validated)
490 unsigned int numDimensions = inputShapes[0].GetNumDimensions();
491 if (outputShape.GetNumDimensions() != numDimensions)
492 {
493 return Fail("%s: Output shape has wrong number of dimensions", __func__);
494 }
495
496 unsigned int outputSizeAlongConcatenatedDimension = 0;
497 for (unsigned int i = 0; i < inputShapes.size(); i++)
498 {
499 outputSizeAlongConcatenatedDimension += inputShapes[i][concatDim];
500 }
501
502 for (unsigned int i = 0; i < numDimensions; ++i)
503 {
504 if (i == concatDim)
505 {
506 if (outputShape[i] != outputSizeAlongConcatenatedDimension)
507 {
508 return Fail(
509 "%s: Invalid output shape for dimension %d (%d != %d)",
510 __func__,
511 i,
512 outputShape[i],
513 outputSizeAlongConcatenatedDimension);
514 }
515 }
516 else
517 {
518 if (outputShape[i] != inputShapes[0][i])
519 {
520 return Fail("%s: Invalid output shape", __func__);
521 }
522 }
523 }
524
525 return true;
526}
527
528bool RequiresReshape(armnn::TensorShape & inputShape)
529{
530 return inputShape.GetNumDimensions() < 3;
531}
532
arovir01b0717b52018-09-05 17:03:25 +0100533void SwizzleInputs(armnn::INetwork& network,
534 std::vector<LayerInputHandle>& inputs,
535 std::vector<armnn::TensorShape>& inputShapes,
536 const armnn::PermutationVector& mapping)
537{
538 if (!mapping.IsEqual(IdentityPermutation4D))
539 {
540 size_t nInputs = inputs.size();
541 for (size_t i=0; i<nInputs; ++i)
542 {
543 // add swizzle layer
544 armnn::IConnectableLayer& swizzleLayer = AddPermuteLayer(network, inputs[i], mapping);
545 auto& outputSlot = swizzleLayer.GetOutputSlot(0);
546 auto& outputInfo = outputSlot.GetTensorInfo();
547 // replace inputs with the swizzled ones
548 inputs[i] = LayerInputHandle(true, &outputSlot, outputInfo);
549 inputShapes[i] = inputs[i].GetTensorInfo().GetShape();
550 }
551 }
552}
553
narpra01f176d5a2018-11-18 20:17:48 +0000554bool CreateConcatPermutationParameters(const unsigned int numberOfDimensions,
555 int32_t & concatDimension,
556 std::pair<armnn::PermutationVector, armnn::PermutationVector> & permutationPair)
arovir01b0717b52018-09-05 17:03:25 +0100557{
narpra01f176d5a2018-11-18 20:17:48 +0000558 bool needPermute = false;
arovir01b0717b52018-09-05 17:03:25 +0100559 BOOST_ASSERT(numberOfDimensions >= 3);
560
561 // ArmNN uses Compute Library subtensors to perform concatenation
narpra01f176d5a2018-11-18 20:17:48 +0000562 // This only works when concatenating along dimension 0, 1 or 3 for a 4-D tensor,
563 // or along dimension 0 or 2 for a 3-D tensor.
564 if (numberOfDimensions == 4 && concatDimension == 2)
arovir01b0717b52018-09-05 17:03:25 +0100565 {
narpra01f176d5a2018-11-18 20:17:48 +0000566 concatDimension = 1;
567 permutationPair = std::make_pair(SwapDim1And2, SwapDim1And2);
568 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100569 }
narpra01f176d5a2018-11-18 20:17:48 +0000570 else if (numberOfDimensions == 3 && concatDimension == 1)
arovir01b0717b52018-09-05 17:03:25 +0100571 {
narpra01f176d5a2018-11-18 20:17:48 +0000572 concatDimension = 0;
573 permutationPair = std::make_pair(RotateTensorLeft, RotateTensorRight);
574 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100575 }
narpra01f176d5a2018-11-18 20:17:48 +0000576 return needPermute;
arovir01b0717b52018-09-05 17:03:25 +0100577}
578
579} // anonymous namespace
580
581namespace armnn_driver
582{
583
584//// Creates an ArmNN activation layer and connects it to the given layer, if the
585//// passed in AndroidNN activation function requires so.
586//// @return The end layer of the sequence of layers built for the given AndroidNN
587//// activation function or nullptr if an error occurred (e.g. unsupported activation).
588//// Note that the end layer matches the input layer if no activation is required
589//// (the sequence of layers has length 1).
590armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
591 ActivationFn activation,
592 armnn::IConnectableLayer* prevLayer,
593 ConversionData& data);
594
595} // namespace armnn_driver
596
597///
598/// Utility templates
599///
600
601namespace armnn_driver
602{
603
604using namespace android::nn;
605
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100606template<typename HalPolicy,
607 typename HalOperand = typename HalPolicy::Operand,
608 typename HalOperation = typename HalPolicy::Operation,
609 typename HalModel = typename HalPolicy::Model>
610const HalOperand* GetInputOperand(const HalOperation& operation,
611 uint32_t inputIndex,
612 const HalModel& model,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100613 bool failOnIndexOutOfBounds = true)
arovir01b0717b52018-09-05 17:03:25 +0100614{
615 if (inputIndex >= operation.inputs.size())
616 {
saoste01b8471482018-10-10 09:44:51 +0100617 if (failOnIndexOutOfBounds)
618 {
619 Fail("%s: invalid input index: %i out of %i", __func__, inputIndex, operation.inputs.size());
620 }
arovir01b0717b52018-09-05 17:03:25 +0100621 return nullptr;
622 }
623
624 BOOST_ASSERT(operation.inputs[inputIndex] < model.operands.size()); // Model should have been validated beforehand
625 return &model.operands[operation.inputs[inputIndex]];
626}
627
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100628template<typename HalPolicy,
629 typename HalOperand = typename HalPolicy::Operand,
630 typename HalOperation = typename HalPolicy::Operation,
631 typename HalModel = typename HalPolicy::Model>
632const HalOperand* GetOutputOperand(const HalOperation& operation,
633 uint32_t outputIndex,
634 const HalModel& model)
arovir01b0717b52018-09-05 17:03:25 +0100635{
636 if (outputIndex >= operation.outputs.size())
637 {
638 Fail("%s: invalid output index: %i out of %i", __func__, outputIndex, operation.outputs.size());
639 return nullptr;
640 }
641
642 // Model should have been validated beforehand
643 BOOST_ASSERT(operation.outputs[outputIndex] < model.operands.size());
644
645 return &model.operands[operation.outputs[outputIndex]];
646}
647
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100648template<typename HalPolicy,
649 typename HalOperand = typename HalPolicy::Operand,
650 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100651const void* GetOperandValueReadOnlyAddress(const HalOperand& operand,
Matthew Bentham912b3622019-05-03 15:49:14 +0100652 const HalModel& model,
653 const ConversionData& data,
Kevin Mayf29a2c52019-03-14 11:56:32 +0000654 bool optional = false)
arovir01b0717b52018-09-05 17:03:25 +0100655{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100656 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
arovir01b0717b52018-09-05 17:03:25 +0100657
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100658 const void* valueStart = nullptr;
arovir01b0717b52018-09-05 17:03:25 +0100659 switch (operand.lifetime)
660 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100661 case HalOperandLifeTime::CONSTANT_COPY:
arovir01b0717b52018-09-05 17:03:25 +0100662 {
663 // Constant found in model.operandValues
664 valueStart = &model.operandValues[operand.location.offset];
665 break;
666 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100667 case HalOperandLifeTime::CONSTANT_REFERENCE:
arovir01b0717b52018-09-05 17:03:25 +0100668 {
669 // Constant specified via a Memory object
670 valueStart = GetMemoryFromPool(operand.location, data.m_MemPools);
671 break;
672 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100673 case HalOperandLifeTime::NO_VALUE:
Kevin Mayf29a2c52019-03-14 11:56:32 +0000674 {
675 // An optional input tensor with no values is not an error so should not register as a fail
676 if (optional)
677 {
678 valueStart = nullptr;
679 break;
680 }
Matthew Bentham912b3622019-05-03 15:49:14 +0100681 [[fallthrough]];
Kevin Mayf29a2c52019-03-14 11:56:32 +0000682 }
arovir01b0717b52018-09-05 17:03:25 +0100683 default:
684 {
685 // Unsupported/invalid (e.g. can't get value of an input to the model)
686 Fail("%s: unsupported/invalid operand lifetime: %s",
687 __func__, toString(operand.lifetime).c_str());
688 valueStart = nullptr;
689 }
690 }
691
692 return valueStart;
693}
694
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100695template<typename HalPolicy,
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100696 typename HalOperation = typename HalPolicy::Operation,
697 typename HalModel = typename HalPolicy::Model,
698 typename HalOperandType = typename HalPolicy::OperandType>
699bool GetOperandType(const HalOperation& operation,
700 uint32_t inputIndex,
701 const HalModel& model,
702 HalOperandType& type)
703{
704 using HalOperand = typename HalPolicy::Operand;
705
706 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
707 if (!operand)
708 {
709 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
710 }
711
712 type = operand->type;
713 return true;
714}
715
716template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100717 typename HalOperand = typename HalPolicy::Operand,
718 typename HalModel = typename HalPolicy::Model>
719ConstTensorPin ConvertOperandToConstTensorPin(const HalOperand& operand,
720 const HalModel& model,
721 const ConversionData& data,
722 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
723 const armnn::TensorShape* overrideTensorShape = nullptr,
724 bool optional = false)
725{
726 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
727
728 if (!IsOperandTypeSupportedForTensors(operand.type))
729 {
730 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand.type).c_str());
731 return ConstTensorPin();
732 }
733
734 if (!optional &&
735 operand.lifetime != HalOperandLifeTime::CONSTANT_COPY &&
736 operand.lifetime != HalOperandLifeTime::CONSTANT_REFERENCE &&
737 operand.lifetime != HalOperandLifeTime::NO_VALUE)
738 {
739 Fail("%s: invalid operand lifetime: %s", __func__, toString(operand.lifetime).c_str());
740 return ConstTensorPin();
741 }
742
743 const void* const valueStart = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data, optional);
744 if (!valueStart)
745 {
746 if (optional)
747 {
748 // optional tensor with no values is not really an error; return it as invalid, but marked as optional
749 return ConstTensorPin(true);
750 }
751 // mandatory tensor with no values
752 Fail("%s: failed to get operand address", __func__);
753 return ConstTensorPin();
754 }
755
756 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(operand);
757 if (overrideTensorShape != nullptr)
758 {
759 tensorInfo.SetShape(*overrideTensorShape);
760 }
761 return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings);
762}
763
764template<typename HalPolicy,
765 typename HalOperation = typename HalPolicy::Operation,
766 typename HalModel = typename HalPolicy::Model>
767ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operation,
768 uint32_t inputIndex,
769 const HalModel& model,
770 const ConversionData& data,
771 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
772 const armnn::TensorShape* overrideTensorShape = nullptr,
773 bool optional = false)
774{
775 using HalOperand = typename HalPolicy::Operand;
776
777 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
778 if (!operand)
779 {
780 Fail("%s: failed to get input operand: index=%u", __func__, inputIndex);
781 return ConstTensorPin();
782 }
783 return ConvertOperandToConstTensorPin<HalPolicy>(*operand,
784 model,
785 data,
786 dimensionMappings,
787 overrideTensorShape,
788 optional);
789}
790
791template<typename HalPolicy,
792 typename OutputType,
793 typename HalOperandType = typename HalPolicy::OperandType,
794 typename HalOperation = typename HalPolicy::Operation,
795 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100796bool GetInputScalar(const HalOperation& operation,
797 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100798 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100799 OutputType& outValue,
800 const HalModel& model,
801 const ConversionData& data)
802{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100803 using HalOperand = typename HalPolicy::Operand;
804
805 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +0100806 if (!operand)
807 {
808 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
809 }
810
811 if (operand->type != type)
812 {
813 return Fail("%s: unexpected operand type: %s (should be %s)",
814 __func__, toString(operand->type).c_str(), toString(type).c_str());
815 }
816
817 if (operand->location.length != sizeof(OutputType))
818 {
819 return Fail("%s: incorrect operand location length: %i (should be %i)",
820 __func__, operand->location.length, sizeof(OutputType));
821 }
822
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100823 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100824 if (!valueAddress)
825 {
826 return Fail("%s: failed to get address for operand", __func__);
827 }
828
829 outValue = *(static_cast<const OutputType*>(valueAddress));
830 return true;
831}
832
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100833template<typename HalPolicy,
834 typename HalOperation = typename HalPolicy::Operation,
835 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100836bool GetInputInt32(const HalOperation& operation,
837 uint32_t inputIndex,
838 int32_t& outValue,
839 const HalModel& model,
840 const ConversionData& data)
841{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100842 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::INT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100843}
844
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100845template<typename HalPolicy,
846 typename HalOperation = typename HalPolicy::Operation,
847 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100848bool GetInputFloat32(const HalOperation& operation,
849 uint32_t inputIndex,
850 float& outValue,
851 const HalModel& model,
852 const ConversionData& data)
853{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100854 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::FLOAT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100855}
856
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100857template<typename HalPolicy,
858 typename HalOperation = typename HalPolicy::Operation,
859 typename HalOperandType = typename HalPolicy::OperandType,
860 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100861bool GetInputActivationFunctionImpl(const HalOperation& operation,
862 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100863 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100864 ActivationFn& outActivationFunction,
865 const HalModel& model,
866 const ConversionData& data)
867{
Mike Kellyb5fdf382019-06-11 16:35:25 +0100868 if (type != HalOperandType::INT32 && type != HalOperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100869 {
870 return Fail("%s: unexpected operand type: %s (should be %s or %s)",
871 __func__,
872 toString(type).c_str(),
873 toString(OperandType::INT32).c_str(),
874 toString(OperandType::TENSOR_INT32).c_str());
875 }
876
877 int32_t activationFunctionAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100878 if (!GetInputScalar<HalPolicy>(operation, inputIndex, type, activationFunctionAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100879 {
880 return Fail("%s: failed to get activation input value", __func__);
881 }
882 outActivationFunction = static_cast<ActivationFn>(activationFunctionAsInt);
883 return true;
884}
885
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100886template<typename HalPolicy,
887 typename HalOperation = typename HalPolicy::Operation,
888 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100889bool GetInputActivationFunction(const HalOperation& operation,
890 uint32_t inputIndex,
891 ActivationFn& outActivationFunction,
892 const HalModel& model,
893 const ConversionData& data)
894{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100895 return GetInputActivationFunctionImpl<HalPolicy>(operation,
896 inputIndex,
897 HalPolicy::OperandType::INT32,
898 outActivationFunction,
899 model,
900 data);
arovir01b0717b52018-09-05 17:03:25 +0100901}
902
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100903template<typename HalPolicy,
904 typename HalOperation = typename HalPolicy::Operation,
905 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100906bool GetInputActivationFunctionFromTensor(const HalOperation& operation,
907 uint32_t inputIndex,
908 ActivationFn& outActivationFunction,
909 const HalModel& model,
910 const ConversionData& data)
911{
912 // This only accepts a 1-D tensor of size 1
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100913 return GetInputActivationFunctionImpl<HalPolicy>(operation,
914 inputIndex,
915 HalPolicy::OperandType::INT32,
916 outActivationFunction,
917 model,
918 data);
arovir01b0717b52018-09-05 17:03:25 +0100919}
920
921
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100922template<typename HalPolicy,
923 typename HalOperation = typename HalPolicy::Operation,
924 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100925bool GetOptionalInputActivation(const HalOperation& operation,
926 uint32_t inputIndex,
927 ActivationFn& activationFunction,
928 const HalModel& model,
929 const ConversionData& data)
930{
931 if (operation.inputs.size() <= inputIndex)
932 {
933 activationFunction = ActivationFn::kActivationNone;
934 }
935 else
936 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100937 if (!GetInputActivationFunction<HalPolicy>(operation, inputIndex, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100938 {
939 return Fail("%s: Operation has invalid inputs", __func__);
940 }
941 }
942 return true;
943}
944
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100945template<typename HalPolicy,
946 typename ConvolutionDescriptor,
947 typename HalOperation = typename HalPolicy::Operation,
948 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100949bool GetOptionalConvolutionDilationParams(const HalOperation& operation,
950 uint32_t dilationXIndex,
951 ConvolutionDescriptor& descriptor,
952 const HalModel& model,
953 const ConversionData& data)
954{
955 bool success = true;
956 if (operation.inputs.size() >= dilationXIndex + 2)
957 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100958 success &= GetInputScalar<HalPolicy>(operation,
959 dilationXIndex,
960 HalPolicy::OperandType::INT32,
961 descriptor.m_DilationX,
962 model,
963 data);
964 success &= GetInputScalar<HalPolicy>(operation,
965 dilationXIndex + 1,
966 HalPolicy::OperandType::INT32,
967 descriptor.m_DilationY,
968 model,
969 data);
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100970 }
971
972 return success;
973}
974
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100975template<typename HalPolicy,
976 typename HalOperand = typename HalPolicy::Operand,
977 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100978bool GetTensorInt32Values(const HalOperand& operand,
arovir01b0717b52018-09-05 17:03:25 +0100979 std::vector<int32_t>& outValues,
980 const HalModel& model,
981 const ConversionData& data)
982{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100983 if (operand.type != HalPolicy::OperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100984 {
985 return Fail("%s: invalid operand type: %s", __func__, toString(operand.type).c_str());
986 }
987
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100988 const void* startAddress = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100989 if (!startAddress)
990 {
991 return Fail("%s: failed to get operand address", __func__, operand.type);
992 }
993
994 // Check number of bytes is sensible
995 const uint32_t numBytes = operand.location.length;
996 if (numBytes % sizeof(int32_t) != 0)
997 {
998 return Fail("%s: invalid number of bytes: %i, expected to be a multiple of %i",
999 __func__, numBytes, sizeof(int32_t));
1000 }
1001
1002 outValues.resize(numBytes / sizeof(int32_t));
1003 memcpy(outValues.data(), startAddress, numBytes);
1004 return true;
1005}
1006
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001007template<typename HalPolicy,
1008 typename HalOperation = typename HalPolicy::Operation,
1009 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001010bool GetInputPaddingScheme(const HalOperation& operation,
1011 uint32_t inputIndex,
1012 PaddingScheme& outPaddingScheme,
1013 const HalModel& model,
1014 const ConversionData& data)
1015{
1016 int32_t paddingSchemeAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001017 if (!GetInputInt32<HalPolicy>(operation, inputIndex, paddingSchemeAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001018 {
1019 return Fail("%s: failed to get padding scheme input value", __func__);
1020 }
1021
1022 outPaddingScheme = static_cast<android::nn::PaddingScheme>(paddingSchemeAsInt);
1023 return true;
1024}
1025
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001026template<typename HalPolicy,
1027 typename HalOperation = typename HalPolicy::Operation,
1028 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001029LayerInputHandle ConvertToLayerInputHandle(const HalOperation& operation,
1030 uint32_t inputIndex,
1031 const HalModel& model,
1032 ConversionData& data)
1033{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001034 using HalOperand = typename HalPolicy::Operand;
Sadik Armagan44bcc022019-06-18 17:21:36 +01001035 using HalOperandType = typename HalPolicy::OperandType;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001036 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
1037
1038 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +01001039 if (!operand)
1040 {
1041 Fail("%s: failed to get input operand %i", __func__, inputIndex);
1042 return LayerInputHandle();
1043 }
1044
1045 if (!IsOperandTypeSupportedForTensors(operand->type))
1046 {
1047 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand->type).c_str());
1048 return LayerInputHandle();
1049 }
1050
Sadik Armagan44bcc022019-06-18 17:21:36 +01001051 try
arovir01b0717b52018-09-05 17:03:25 +01001052 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001053 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001054 if (IsDynamicTensor(operandTensorInfo))
1055 {
1056 Fail("%s: dynamic input tensors are not supported", __func__);
1057 return LayerInputHandle();
1058 }
arovir01b0717b52018-09-05 17:03:25 +01001059
Sadik Armagan44bcc022019-06-18 17:21:36 +01001060 switch (operand->lifetime)
arovir01b0717b52018-09-05 17:03:25 +01001061 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001062 case HalOperandLifeTime::MODEL_INPUT:
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001063 {
1064 // NOTE: We must check whether we can support the input tensor on at least one
1065 // of the provided backends; otherwise we cannot convert the operation
1066 bool isInputSupported = false;
1067 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1068 IsInputSupported,
1069 data.m_Backends,
1070 isInputSupported,
1071 operandTensorInfo);
1072
1073 if (!isInputSupported)
1074 {
1075 Fail("%s: unsupported input tensor", __func__);
1076 return LayerInputHandle();
1077 }
1078
1079 BOOST_FALLTHROUGH; // intentional fallthrough
1080 }
1081 case HalOperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001082 case HalOperandLifeTime::MODEL_OUTPUT:
arovir01b0717b52018-09-05 17:03:25 +01001083 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001084 // The tensor is either an operand internal to the model, or a model input.
1085 // It can be associated with an ArmNN output slot for an existing layer.
1086
1087 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
1088 const uint32_t operandIndex = operation.inputs[inputIndex];
1089 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
Sadik Armagan44bcc022019-06-18 17:21:36 +01001090 }
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001091 case HalOperandLifeTime::CONSTANT_COPY: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001092 case HalOperandLifeTime::CONSTANT_REFERENCE:
1093 {
1094 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
1095 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin<HalPolicy>(*operand, model, data);
1096 if (tensorPin.IsValid())
arovir01b0717b52018-09-05 17:03:25 +01001097 {
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001098 bool isSupported = false;
1099 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1100 IsConstantSupported,
1101 data.m_Backends,
1102 isSupported,
1103 tensorPin.GetConstTensor().GetInfo());
Mike Kelly28e3d9f2019-08-07 14:55:04 +01001104 if (!isSupported)
Sadik Armagan44bcc022019-06-18 17:21:36 +01001105 {
1106 return LayerInputHandle();
1107 }
1108
1109 armnn::IConnectableLayer* constantLayer =
1110 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
1111 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
1112 outputSlot.SetTensorInfo(tensorPin.GetConstTensor().GetInfo());
1113
1114 return LayerInputHandle(true, &outputSlot, operandTensorInfo);
1115 }
1116 else
1117 {
1118 Fail("%s: invalid operand tensor", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001119 return LayerInputHandle();
1120 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001121 break;
arovir01b0717b52018-09-05 17:03:25 +01001122 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001123 default:
arovir01b0717b52018-09-05 17:03:25 +01001124 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001125 // Unsupported lifetime for an input tensor
1126 Fail("%s: unsupported lifetime for input tensor: %s",
1127 __func__, toString(operand->lifetime).c_str());
arovir01b0717b52018-09-05 17:03:25 +01001128 return LayerInputHandle();
1129 }
arovir01b0717b52018-09-05 17:03:25 +01001130 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001131 }
1132 catch (UnsupportedOperand<HalOperandType>& e)
1133 {
1134 Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str());
1135 return LayerInputHandle();
arovir01b0717b52018-09-05 17:03:25 +01001136 }
1137}
1138
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001139template<typename HalPolicy,
1140 typename HalOperation = typename HalPolicy::Operation,
1141 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001142bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1143 uint32_t operationOutputIndex,
1144 armnn::IConnectableLayer& layer,
1145 uint32_t layerOutputIndex,
1146 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001147 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001148{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001149 using HalOperand = typename HalPolicy::Operand;
1150
1151 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001152 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
1153 {
1154 return false;
1155 }
1156
1157 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
1158
1159 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
1160 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
1161
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001162 outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand));
Mike Kellyb5fdf382019-06-11 16:35:25 +01001163
1164 return true;
1165}
1166
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001167template<typename HalPolicy,
1168 typename HalOperation = typename HalPolicy::Operation,
1169 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001170armnn::DataLayout OptionalDataLayout(const HalOperation& operation,
1171 uint32_t inputIndex,
1172 const HalModel& model,
1173 ConversionData& data)
1174{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001175 using HalOperand = typename HalPolicy::Operand;
1176
1177 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001178 if (!operand)
1179 {
1180 return armnn::DataLayout::NHWC;
1181 }
1182
1183 if (!IsBool(*operand))
1184 {
1185 return armnn::DataLayout::NHWC;
1186 }
1187
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001188 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001189 if (!valueAddress)
1190 {
1191 return armnn::DataLayout::NHWC;
1192 }
1193
1194 if (*(static_cast<const bool*>(valueAddress)))
1195 {
1196 return armnn::DataLayout::NCHW;
1197 }
1198 else
1199 {
1200 return armnn::DataLayout::NHWC;
1201 }
1202}
1203
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001204template<typename HalPolicy,
1205 typename HalOperation = typename HalPolicy::Operation,
1206 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001207bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1208 uint32_t outputIndex,
1209 armnn::IConnectableLayer& layer,
1210 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001211 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001212{
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001213 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1214 outputIndex,
1215 layer,
1216 outputIndex,
1217 model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001218 data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001219}
1220
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001221template<typename HalPolicy,
1222 typename HalOperation = typename HalPolicy::Operation,
1223 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001224bool ConvertToActivation(const HalOperation& operation,
1225 const char* operationName,
1226 const armnn::ActivationDescriptor& activationDesc,
1227 const HalModel& model,
1228 ConversionData& data)
1229{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001230 using HalOperand = typename HalPolicy::Operand;
1231
1232 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001233 if (!input.IsValid())
1234 {
1235 return Fail("%s: Input 0 is invalid", operationName);
1236 }
1237
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001238 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001239 if (!outputOperand)
1240 {
1241 return false;
1242 }
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001243
1244 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan2050c232019-07-23 16:59:58 +01001245 if (IsDynamicTensor(outInfo))
1246 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001247 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan2050c232019-07-23 16:59:58 +01001248 }
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001249
1250 bool isSupported = false;
1251 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1252 IsActivationSupported,
1253 data.m_Backends,
1254 isSupported,
1255 input.GetTensorInfo(),
1256 outInfo,
1257 activationDesc);
1258 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001259 {
1260 return false;
1261 }
1262
1263 armnn::IConnectableLayer* layer = data.m_Network->AddActivationLayer(activationDesc);
1264 BOOST_ASSERT(layer != nullptr);
1265 input.Connect(layer->GetInputSlot(0));
1266
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001267 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001268}
1269
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001270template<typename HalPolicy,
Sadik Armagan61113162019-07-25 09:09:40 +01001271 typename HalOperation = typename HalPolicy::Operation,
1272 typename HalModel = typename HalPolicy::Model>
1273bool ConvertReLu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1274{
1275 armnn::ActivationDescriptor desc;
1276 desc.m_Function = armnn::ActivationFunction::ReLu;
1277
1278 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1279}
1280
1281template<typename HalPolicy,
1282 typename HalOperation = typename HalPolicy::Operation,
1283 typename HalModel = typename HalPolicy::Model>
1284bool ConvertReLu1(const HalOperation& operation, const HalModel& model, ConversionData& data)
1285{
1286 armnn::ActivationDescriptor desc;
1287 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1288 desc.m_A = 1.0f;
1289 desc.m_B = -1.0f;
1290
1291 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1292}
1293
1294template<typename HalPolicy,
1295 typename HalOperation = typename HalPolicy::Operation,
1296 typename HalModel = typename HalPolicy::Model>
1297bool ConvertReLu6(const HalOperation& operation, const HalModel& model, ConversionData& data)
1298{
1299 armnn::ActivationDescriptor desc;
1300 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1301 desc.m_A = 6.0f;
1302
1303 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1304}
1305
1306template<typename HalPolicy,
1307 typename HalOperation = typename HalPolicy::Operation,
1308 typename HalModel = typename HalPolicy::Model>
1309bool ConvertTanH(const HalOperation& operation, const HalModel& model, ConversionData& data)
1310{
1311 armnn::ActivationDescriptor desc;
1312 desc.m_Function = armnn::ActivationFunction::TanH;
1313 desc.m_A = 1.0f; // android nn does not support tanH parameters
1314 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1315
1316 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1317}
1318
1319template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001320 typename HalOperation = typename HalPolicy::Operation,
1321 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001322bool ConvertPaddings(const HalOperation& operation,
1323 const HalModel& model,
1324 ConversionData& data,
1325 unsigned int rank,
1326 armnn::PadDescriptor& padDescriptor)
1327{
1328 using HalOperand = typename HalPolicy::Operand;
1329
1330 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
1331 if (!paddingsOperand)
1332 {
1333 return Fail("%s: Could not read paddings operand", __func__);
1334 }
1335
1336 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
1337 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2)
1338 {
1339 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
1340 }
1341
1342 std::vector<int32_t> paddings;
1343 GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data);
1344
1345 // add padding for each dimension of input tensor.
1346 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
1347 {
1348 int paddingBeforeInput = paddings[i];
1349 int paddingAfterInput = paddings[i + 1];
1350
1351 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
1352 {
1353 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
1354 }
1355
1356 padDescriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
1357 }
1358
1359 return true;
1360}
1361
1362template<typename HalPolicy,
1363 typename HalOperation = typename HalPolicy::Operation,
1364 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001365bool ConvertPooling2d(const HalOperation& operation,
1366 const char* operationName,
1367 armnn::PoolingAlgorithm poolType,
1368 const HalModel& model,
1369 ConversionData& data)
1370{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001371 using HalOperand = typename HalPolicy::Operand;
1372 using HalOperandType = typename HalPolicy::OperandType;
1373
1374 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001375 if (!input.IsValid())
1376 {
1377 return Fail("%s: Could not read input 0", operationName);
1378 }
1379
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001380 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001381 if (!output)
1382 {
1383 return Fail("%s: Could not read output 0", __func__);
1384 }
1385
1386 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1387 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1388
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001389 if (IsDynamicTensor(outputInfo))
1390 {
1391 return Fail("%s: Dynamic output tensors are not supported", __func__);
1392 }
1393
arovir01b0717b52018-09-05 17:03:25 +01001394 armnn::Pooling2dDescriptor desc;
1395 desc.m_PoolType = poolType;
1396 desc.m_OutputShapeRounding = armnn::OutputShapeRounding::Floor;
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001397 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001398
1399 ActivationFn activation;
1400
Sadik Armagan15d63e22019-07-26 16:59:35 +01001401 auto inputSize = operation.inputs.size();
1402
1403 if (inputSize >= 10)
1404 {
1405 // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type)
1406 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1407 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1408 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1409 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1410 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1411 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1412 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1413 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1414 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
1415 {
1416 return Fail("%s: Operation has invalid inputs", operationName);
1417 }
1418
1419 if (Is12Operand(*output))
1420 {
1421 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
1422 }
1423 }
1424 else
arovir01b0717b52018-09-05 17:03:25 +01001425 {
1426 // one input, 6 parameters (padding, stridex, stridey, width, height, activation type)
1427 android::nn::PaddingScheme scheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001428 if (!GetInputPaddingScheme<HalPolicy>(operation, 1, scheme, model, data) ||
1429 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1430 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1431 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1432 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1433 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001434 {
1435 return Fail("%s: Operation has invalid inputs", operationName);
1436 }
1437
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001438 const unsigned int inputWidth = inputInfo.GetShape()[2];
1439 const unsigned int inputHeight = inputInfo.GetShape()[1];
arovir01b0717b52018-09-05 17:03:25 +01001440
1441 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, scheme);
1442 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, scheme);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001443
1444 if (Is12Operand(*output))
arovir01b0717b52018-09-05 17:03:25 +01001445 {
Sadik Armagan15d63e22019-07-26 16:59:35 +01001446 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001447 }
1448 }
1449
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001450 bool isSupported = false;
1451 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1452 IsPooling2dSupported,
1453 data.m_Backends,
1454 isSupported,
1455 inputInfo,
1456 outputInfo,
1457 desc);
1458 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001459 {
Éanna Ó Catháin3d1059c2018-10-11 15:53:04 +01001460 return false;
arovir01b0717b52018-09-05 17:03:25 +01001461 }
arovir01b0717b52018-09-05 17:03:25 +01001462
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001463 armnn::IConnectableLayer* pooling2dLayer = data.m_Network->AddPooling2dLayer(desc);
1464 if (!pooling2dLayer)
arovir01b0717b52018-09-05 17:03:25 +01001465 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001466 return Fail("%s: AddPooling2dLayer failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001467 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001468
1469 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, pooling2dLayer, data);
1470 if (!endLayer)
arovir01b0717b52018-09-05 17:03:25 +01001471 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001472 return Fail("%s: ProcessActivation failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001473 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001474
1475 input.Connect(pooling2dLayer->GetInputSlot(0));
1476
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001477 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001478}
1479
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001480template<typename HalPolicy,
Mike Kellyb8805202019-07-31 17:25:43 +01001481 typename Operation = typename HalPolicy::Operation,
1482 typename Model = typename HalPolicy::Model>
Mike Kelly46272802019-08-14 17:00:48 +01001483bool ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
1484{
1485 using Operand = typename HalPolicy::Operand;
1486
1487 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1488 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1489
1490 if (!input0.IsValid() || !input1.IsValid())
1491 {
1492 return Fail("%s: Operation has invalid inputs", __func__);
1493 }
1494
1495 // The FuseActivation parameter is always the input index 2
1496 // and it should be optional
1497 ActivationFn activationFunction;
1498 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
1499 {
1500 return Fail("%s: Operation has invalid inputs", __func__);
1501 }
1502
1503 const Operand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1504 if (!outputOperand)
1505 {
1506 return false;
1507 }
1508
1509 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
1510 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
1511
1512 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
1513 if (IsDynamicTensor(outputInfo))
1514 {
1515 return Fail("%s: Dynamic output tensors are not supported", __func__);
1516 }
1517
1518 bool isSupported = false;
1519 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1520 IsAdditionSupported,
1521 data.m_Backends,
1522 isSupported,
1523 inputInfo0,
1524 inputInfo1,
1525 outputInfo);
1526 if (!isSupported)
1527 {
1528 return false;
1529 }
1530
1531 armnn::IConnectableLayer* const startLayer = data.m_Network->AddAdditionLayer();
1532 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
1533
1534 if (endLayer != nullptr)
1535 {
Sadik Armagan64b19b52019-08-19 09:49:58 +01001536 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
1537 if (!isReshapeSupported)
1538 {
1539 return false;
1540 }
1541
Mike Kelly46272802019-08-14 17:00:48 +01001542 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1543 }
1544 else
1545 {
1546 return Fail("%s: ProcessActivation failed", __func__);
1547 }
1548}
1549
1550template<typename HalPolicy,
1551 typename Operation = typename HalPolicy::Operation,
1552 typename Model = typename HalPolicy::Model>
Mike Kellyb8805202019-07-31 17:25:43 +01001553bool ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
1554{
1555 using HalOperand = typename HalPolicy::Operand;
1556 using HalOperandType = typename HalPolicy::OperandType;
1557
1558 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
1559 if (operation.inputs.size() <= 1)
1560 {
1561 return Fail("%s: Operation has insufficient arguments", __func__);
1562 }
1563
1564 // Get inputs and outputs
1565 const std::size_t numInputTensors = operation.inputs.size() - 1;
1566
1567 int32_t concatDim;
1568 if (!GetInputScalar<HalPolicy>(operation, numInputTensors, HalOperandType::INT32, concatDim, model, data))
1569 {
1570 return Fail("%s: Operation has invalid inputs", __func__);
1571 }
1572
1573 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1574 if (!outputOperand)
1575 {
1576 return Fail("%s: Operation has no outputs", __func__);
1577 }
1578
1579
1580 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
1581 armnn::TensorShape outputShape = outputInfo.GetShape();
1582
1583 //
1584 // handle negative concat dims along the lines of tensorflow as described here:
1585 // https://www.tensorflow.org/api_docs/python/tf/concat
1586 // "negative axis refers to axis + rank(values)-th dimension"
1587 //
1588 if (concatDim < 0)
1589 {
1590 concatDim += outputShape.GetNumDimensions();
1591 }
1592
1593 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
1594 {
1595 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
1596 }
1597
1598 std::vector<LayerInputHandle> inputHandles;
1599 std::vector<armnn::TensorShape> inputShapes;
1600
1601 inputHandles.reserve(numInputTensors);
1602 inputShapes.reserve(numInputTensors);
1603
1604 bool inputsHaveBeenReshaped = false;
1605 unsigned int tensorDimensionsAdded = 0;
1606
1607 for (uint32_t i = 0; i < numInputTensors; ++i)
1608 {
1609 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, i, model);
1610 if (!operand)
1611 {
1612 return Fail("%s: Operation has invalid inputs", __func__);
1613 }
1614
Teresa Charlin3b959602019-10-31 17:05:47 +00001615 LayerInputHandle operandInputHandle = ConvertToLayerInputHandle<HalPolicy>(operation, i, model, data);
1616 if (!operandInputHandle.IsValid())
1617 {
1618 return Fail("%s: Operation has invalid inputs", __func__);
1619 }
Mike Kellyb8805202019-07-31 17:25:43 +01001620
Teresa Charlin3b959602019-10-31 17:05:47 +00001621 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
Mike Kellyb8805202019-07-31 17:25:43 +01001622 if (operandShape.GetNumDimensions() == 0)
1623 {
1624 return Fail("%s: Operands with rank 0 are not supported", __func__);
1625 }
1626
1627 if (RequiresReshape(operandShape))
1628 {
1629 inputsHaveBeenReshaped = true;
1630
1631 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
1632
1633 // Expand the tensor to three dimensions
1634 if (operandShape.GetNumDimensions() == 2)
1635 {
1636 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
1637 tensorDimensionsAdded = 1;
1638 }
1639 else
1640 {
1641 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
1642 tensorDimensionsAdded = 2;
1643 }
1644
1645 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
1646 *data.m_Network,
1647 operandInputHandle,
1648 reshapeInfo
1649 );
1650
1651 // Point to the reshape operation rather then the input operation
1652 operandShape = reshapeInfo.GetShape();
1653 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
1654 }
1655
1656 inputShapes.emplace_back(operandShape);
1657 inputHandles.emplace_back(operandInputHandle);
1658
1659 if (!inputHandles.back().IsValid())
1660 {
1661 return Fail("%s: Operation has invalid inputs", __func__);
1662 }
1663 }
1664
1665 BOOST_ASSERT(inputShapes.size() == inputHandles.size());
1666
1667 if (inputsHaveBeenReshaped)
1668 {
1669 // Adjust the concatenation dimension by the amount of dimensions added (if any)
1670 concatDim += tensorDimensionsAdded;
1671
1672 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
1673 if (tensorDimensionsAdded == 1)
1674 {
1675 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
1676 }
1677 else if (tensorDimensionsAdded == 2)
1678 {
1679 outputShape = armnn::TensorShape({1, 1, outputShape[0]});
1680 }
1681 }
1682
1683 // Check if permutations is required and get the pair of permutations required for the concatenation.
1684 // Permutation is required when the concat dimension is 2 for a 4D tensor or 1 for a 3D tensor.
1685 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
1686 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
1687
1688 bool needPermute =
1689 CreateConcatPermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
1690
1691 if (needPermute)
1692 {
1693 outputShape = armnnUtils::Permuted(outputShape, permutationPair.first);
1694 }
1695
1696 outputInfo.SetShape(outputShape);
1697
1698 // this is no-op for identity swizzles, otherwise it replaces both
1699 // the handles and shapes with the swizzled layer output handles and shapes
1700 SwizzleInputs(*data.m_Network, inputHandles, inputShapes, permutationPair.first);
1701
1702 // Create an armnn concat layer descriptor - this will also perform validation on the input shapes
1703 armnn::OriginsDescriptor concatDescriptor;
1704
1705 try
1706 {
1707 // The concat descriptor is always created across the only supported concat dimension
1708 // which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
1709 concatDescriptor =
1710 armnn::CreateDescriptorForConcatenation(inputShapes.begin(), inputShapes.end(), concatDim);
1711 }
1712 catch (const armnn::Exception& error)
1713 {
1714 return Fail("%s: Error preparing concat descriptor. %s", __func__, error.what());
1715 }
1716
1717 // Validate the output shape is correct given the input shapes based on the
1718 // only valid concat dimension which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
1719 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
1720 {
1721 return Fail("%s: Error validating the output shape for concat", __func__);
1722 }
1723
1724 std::vector<const armnn::TensorInfo*> inputTensorInfos;
1725 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
1726 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
1727
1728 bool isSupported = false;
1729 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1730 IsConcatSupported,
1731 data.m_Backends,
1732 isSupported,
1733 inputTensorInfos,
1734 outputInfo,
1735 concatDescriptor);
1736 if (!isSupported)
1737 {
1738 return false;
1739 }
1740
1741 armnn::IConnectableLayer* layer = data.m_Network->AddConcatLayer(concatDescriptor);
1742 assert(layer != nullptr);
1743 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1744
1745 // Connect inputs to the layer
1746 const int numInputSlots = layer->GetNumInputSlots();
1747 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
1748 for (int i = 0; i < numInputSlots; ++i)
1749 {
1750 // connect the input directly to the merge (concat) layer
1751 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
1752 }
1753
1754 if (needPermute)
1755 {
1756 // Add permutation layer and connect the output to it, the permutation becomes the output layer
1757 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(*data.m_Network,
1758 layer->GetOutputSlot(0),
1759 permutationPair.second);
1760 layer = &deswizzleLayer;
1761 }
1762
1763 if (inputsHaveBeenReshaped)
1764 {
1765 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
1766
1767 // Undo the reshape knowing the amount of dimensions added
1768 if (tensorDimensionsAdded == 1)
1769 {
1770 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
1771 afterConcatInfo.GetShape()[2] }));
1772 }
1773 else if (tensorDimensionsAdded == 2)
1774 {
1775 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2] }));
1776 }
1777
1778 layer = &AddReshapeLayer(
1779 *data.m_Network,
1780 layer->GetOutputSlot(0),
1781 afterConcatInfo
1782 );
1783 }
1784
1785 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1786}
1787
1788template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001789 typename HalOperation = typename HalPolicy::Operation,
1790 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001791bool ConvertConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1792{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001793 using HalOperand = typename HalPolicy::Operand;
1794 using HalOperandType = typename HalPolicy::OperandType;
1795
1796 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001797 if (!input.IsValid())
1798 {
1799 return Fail("%s: Operation has invalid inputs", __func__);
1800 }
1801
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001802 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001803 if (!output)
1804 {
1805 return Fail("%s: Could not read output 0", __func__);
1806 }
1807
1808 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001809 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001810
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001811 if (IsDynamicTensor(outputInfo))
1812 {
1813 return Fail("%s: Dynamic output tensors are not supported", __func__);
1814 }
1815
Mike Kellyb5fdf382019-06-11 16:35:25 +01001816 // ArmNN does not currently support non-fixed weights or bias
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001817 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1818 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001819
1820 if (!weightsPin.IsValid() || !biasPin.IsValid())
1821 {
1822 return Fail("%s: Operation has invalid inputs", __func__);
1823 }
1824
1825 armnn::ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001826 armnn::ConstTensor bias = biasPin.GetConstTensor();
Mike Kellyb5fdf382019-06-11 16:35:25 +01001827 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1828
1829 armnn::Convolution2dDescriptor desc;
1830 desc.m_DataLayout = armnn::DataLayout::NHWC;
1831 ActivationFn activation;
1832
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001833 if (operation.inputs.size() == 10)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001834 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001835 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1836 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1837 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1838 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1839 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1840 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001841 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001842 {
1843 return Fail("%s: Operation has invalid inputs", __func__);
1844 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01001845 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001846 else if (operation.inputs.size() == 7)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001847 {
1848 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001849 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1850 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1851 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001852 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001853 {
1854 return Fail("%s: Operation has invalid inputs", __func__);
1855 }
1856
1857 const uint32_t kernelX = weights.GetShape()[2];
1858 const uint32_t kernelY = weights.GetShape()[1];
1859 const uint32_t inputX = inputInfo.GetShape()[2];
1860 const uint32_t inputY = inputInfo.GetShape()[1];
1861
1862 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1863 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001864 }
1865 else
1866 {
1867 return Fail("%s: Unsupported number of operation inputs", __func__);
1868 }
1869
1870 desc.m_BiasEnabled = true;
1871 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1872
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001873 bool isSupported = false;
1874 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1875 IsConvolution2dSupported,
1876 data.m_Backends,
1877 isSupported,
1878 inputInfo,
1879 outputInfo,
1880 desc,
1881 weights.GetInfo(),
1882 biases);
1883 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001884 {
1885 return false;
1886 }
1887
1888 armnn::IConnectableLayer* startLayer =
1889 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1890
1891 if (!startLayer)
1892 {
1893 return Fail("%s: AddConvolution2dLayer failed", __func__);
1894 }
1895
1896 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1897
1898 if (!endLayer)
1899 {
1900 return Fail("%s: ProcessActivation failed", __func__);
1901 }
1902
1903 input.Connect(startLayer->GetInputSlot(0));
1904
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001905 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001906}
1907
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001908template<typename HalPolicy,
1909 typename HalOperation = typename HalPolicy::Operation,
1910 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +01001911bool ConvertDepthToSpace(const HalOperation& operation, const HalModel& model, ConversionData& data)
1912{
1913 using HalOperand = typename HalPolicy::Operand;
1914 using HalOperandType = typename HalPolicy::OperandType;
1915
1916 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1917 if (!input.IsValid() )
1918 {
1919 return Fail("%s: Operation has invalid inputs", __func__);
1920 }
1921
1922 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1923 unsigned int rank = inputInfo.GetNumDimensions();
1924 if (rank != 4)
1925 {
1926 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1927 }
1928
1929 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1930 if (!output)
1931 {
1932 return Fail("%s: Could not read output 0", __func__);
1933 }
1934
1935 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1936 if (IsDynamicTensor(outputInfo))
1937 {
1938 return Fail("%s: Dynamic output tensors are not supported", __func__);
1939 }
1940
1941 armnn::DepthToSpaceDescriptor descriptor;
1942
1943 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, descriptor.m_BlockSize, model, data);
1944 if (descriptor.m_BlockSize <= 1)
1945 {
1946 return Fail("%s: Block size must be at least 1 in all dimensions");
1947 }
1948
1949 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
1950 if (Is12Operand(*output))
1951 {
1952 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
1953 }
1954
1955 bool isSupported = false;
1956 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1957 IsDepthToSpaceSupported,
1958 data.m_Backends,
1959 isSupported,
1960 inputInfo,
1961 outputInfo,
1962 descriptor);
1963 if (!isSupported)
1964 {
1965 return false;
1966 }
1967
1968 armnn::IConnectableLayer* const layer = data.m_Network->AddDepthToSpaceLayer(descriptor);
1969 assert(layer != nullptr);
1970 input.Connect(layer->GetInputSlot(0));
1971
1972 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1973}
1974
1975template<typename HalPolicy,
1976 typename HalOperation = typename HalPolicy::Operation,
1977 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001978bool ConvertDepthwiseConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1979{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001980 using HalOperand = typename HalPolicy::Operand;
1981 using HalOperandType = typename HalPolicy::OperandType;
1982
1983 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001984
1985 if (!input.IsValid())
1986 {
1987 return Fail("%s: Operation has invalid inputs", __func__);
1988 }
1989
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001990 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001991
1992 if (!output)
1993 {
1994 return Fail("%s: Could not read output 0", __func__);
1995 }
1996
1997 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001998 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001999
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002000 if (IsDynamicTensor(outputInfo))
2001 {
2002 return Fail("%s: Dynamic output tensors are not supported", __func__);
2003 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01002004
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002005 // ArmNN does not currently support non-fixed weights or bias
Mike Kellyb5fdf382019-06-11 16:35:25 +01002006 // 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 +01002007 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002008
2009 if (weightsOperand == nullptr)
2010 {
2011 return Fail("%s: Operand is invalid", __func__);
2012 }
2013 armnn::DepthwiseConvolution2dDescriptor desc;
2014 desc.m_DataLayout = armnn::DataLayout::NHWC;
2015
Mike Kellyb5fdf382019-06-11 16:35:25 +01002016 // Reinterpret weight data as [ H, W, I, M ]
2017 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
2018 weightsOperand->dimensions[2],
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002019 inputInfo.GetShape()[3],
2020 weightsOperand->dimensions[3] / inputInfo.GetShape()[3] });
Mike Kellyb5fdf382019-06-11 16:35:25 +01002021
2022 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
2023 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
2024
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002025 const ConstTensorPin weightsPin =
2026 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2027 1,
2028 model,
2029 data,
2030 HWIMToMIHW,
2031 &weightsShape);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002032
2033 // Bias is a 1D tensor
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002034 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002035
2036 if (!weightsPin.IsValid() || !biasPin.IsValid())
2037 {
2038 return Fail("%s: Operation has invalid inputs", __func__);
2039 }
2040
2041 armnn::ConstTensor weights = weightsPin.GetConstTensor();
2042 armnn::ConstTensor bias = biasPin.GetConstTensor();
2043 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2044
2045 ActivationFn activation;
2046
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002047 if (operation.inputs.size() == 11)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002048 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002049 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2050 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2051 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2052 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2053 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2054 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002055 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002056 {
2057 return Fail("%s: Operation has invalid inputs", __func__);
2058 }
2059 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002060 else if (operation.inputs.size() == 8)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002061 {
2062 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002063 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
2064 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2065 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002066 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002067 {
2068 return Fail("%s: Operation has invalid inputs", __func__);
2069 }
2070
2071 const uint32_t kernelX = weights.GetShape()[3];
2072 const uint32_t kernelY = weights.GetShape()[2];
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002073 const uint32_t inputX = inputInfo.GetShape()[2];
2074 const uint32_t inputY = inputInfo.GetShape()[1];
Mike Kellyb5fdf382019-06-11 16:35:25 +01002075
2076 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
2077 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
2078 }
2079 else
2080 {
2081 return Fail("%s: Unsupported number of operation inputs", __func__);
2082 }
2083
2084 desc.m_BiasEnabled = true;
2085 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
2086
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002087 bool isSupported = false;
2088 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2089 IsDepthwiseConvolutionSupported,
2090 data.m_Backends,
2091 isSupported,
2092 inputInfo,
2093 outputInfo,
2094 desc,
2095 weights.GetInfo(),
2096 biases);
2097 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002098 {
2099 return false;
2100 }
2101
2102 armnn::IConnectableLayer* startLayer =
2103 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2104 if (!startLayer)
2105 {
2106 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
2107 }
2108
2109 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2110 if (!endLayer)
2111 {
2112 return Fail("%s: ProcessActivation failed", __func__);
2113 }
2114
2115 input.Connect(startLayer->GetInputSlot(0));
2116
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002117 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01002118}
2119
Mike Kelly3c673942019-07-25 09:26:06 +01002120template<typename HalPolicy,
Mike Kelly46272802019-08-14 17:00:48 +01002121 typename Operation = typename HalPolicy::Operation,
2122 typename Model = typename HalPolicy::Model>
2123bool ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
Mike Kelly3c673942019-07-25 09:26:06 +01002124{
Mike Kelly46272802019-08-14 17:00:48 +01002125 using Operand = typename HalPolicy::Operand;
2126
2127 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2128 if (!input.IsValid())
2129 {
2130 return Fail("%s: Operation has invalid input", __func__);
2131 }
2132
2133 const Operand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2134 if (!outputOperand)
2135 {
2136 return Fail("%s: Operation has invalid outputs", __func__);
2137 }
2138
2139 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2140 if (IsDynamicTensor(outputInfo))
2141 {
2142 return Fail("%s: Dynamic output tensors are not supported", __func__);
2143 }
2144
2145 bool isSupported = false;
2146 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2147 IsDequantizeSupported,
2148 data.m_Backends,
2149 isSupported,
2150 input.GetTensorInfo(),
2151 GetTensorInfoForOperand(*outputOperand));
2152 if (!isSupported)
2153 {
2154 return false;
2155 }
2156
2157 armnn::IConnectableLayer* const layer = data.m_Network->AddDequantizeLayer();
2158 assert(layer != nullptr);
2159 input.Connect(layer->GetInputSlot(0));
2160
2161 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2162}
2163
2164template<typename HalPolicy,
2165 typename Operation = typename HalPolicy::Operation,
2166 typename Model = typename HalPolicy::Model>
2167bool ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
2168{
2169 using Operand = typename HalPolicy::Operand;
2170
2171 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2172 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
2173
2174 if (!input0.IsValid() || !input1.IsValid())
2175 {
2176 return Fail("%s: Operation has invalid inputs", __func__);
2177 }
2178
2179 // The FuseActivation parameter is always the input index 2
2180 // and it should be optional
2181 ActivationFn activationFunction;
2182 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2183 {
2184 return Fail("%s: Operation has invalid inputs", __func__);
2185 }
2186
2187 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2188 if (!output)
2189 {
2190 return Fail("%s: Could not read output 0", __func__);
2191 }
2192
2193 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2194 if (IsDynamicTensor(outputInfo))
2195 {
2196 return Fail("%s: Dynamic output tensors are not supported", __func__);
2197 }
2198
2199 bool isSupported = false;
2200 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2201 IsDivisionSupported,
2202 data.m_Backends,
2203 isSupported,
2204 input0.GetTensorInfo(),
2205 input1.GetTensorInfo(),
2206 outputInfo);
2207 if (!isSupported)
2208 {
2209 return false;
2210 }
2211
2212 armnn::IConnectableLayer* const startLayer = data.m_Network->AddDivisionLayer();
2213 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2214
2215 if (endLayer)
2216 {
Sadik Armagan64b19b52019-08-19 09:49:58 +01002217 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
2218 if (!isReshapeSupported)
2219 {
2220 return false;
2221 }
2222
Mike Kelly46272802019-08-14 17:00:48 +01002223 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2224 }
2225 return Fail("%s: ProcessActivation failed", __func__);
2226}
2227
2228template<typename HalPolicy,
2229 typename Operation = typename HalPolicy::Operation,
2230 typename Model = typename HalPolicy::Model>
2231bool ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
2232{
2233 using Operand = typename HalPolicy::Operand;
2234
2235 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2236 if (!input.IsValid())
2237 {
2238 return Fail("%s: Operation has invalid inputs", __func__);
2239 }
2240
2241 const Operand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2242 if (!outputOperand)
2243 {
2244 return Fail("%s: Operation has invalid outputs", __func__);
2245 }
2246
2247 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2248 if (IsDynamicTensor(outputInfo))
2249 {
2250 return Fail("%s: Dynamic output tensors are not supported", __func__);
2251 }
2252
2253 bool isSupported = false;
2254 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2255 IsFloorSupported,
2256 data.m_Backends,
2257 isSupported,
2258 input.GetTensorInfo(),
2259 outputInfo);
2260 if (!isSupported)
2261 {
2262 return false;
2263 }
2264
2265 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
2266 assert(layer != nullptr);
2267 input.Connect(layer->GetInputSlot(0));
2268
2269 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2270}
2271
2272template<typename HalPolicy,
2273 typename Operation = typename HalPolicy::Operation,
2274 typename Model = typename HalPolicy::Model>
2275bool ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
2276{
2277 using Operand = typename HalPolicy::Operand;
2278
2279 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2280 if (!input.IsValid())
2281 {
2282 return Fail("%s: Operation has invalid inputs", __func__);
2283 }
2284
2285 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2286 if (!output)
2287 {
2288 return Fail("%s: Could not read output 0", __func__);
2289 }
2290
2291 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2292 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2293
2294 if (IsDynamicTensor(outputInfo))
2295 {
2296 return Fail("%s: Dynamic output tensors are not supported", __func__);
2297 }
2298
2299 // ArmNN does not currently support non-fixed weights or bias
2300 ConstTensorPin weightsPin =
2301 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data); // 2D
2302 ConstTensorPin biasPin =
2303 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data); // 1D
2304
2305 if (!weightsPin.IsValid() || !biasPin.IsValid())
2306 {
2307 return Fail("%s: Operation has invalid inputs", __func__);
2308 }
2309
2310 armnn::ConstTensor weights = weightsPin.GetConstTensor();
2311 armnn::ConstTensor bias = biasPin.GetConstTensor();
2312 armnn::TensorInfo reshapedInfo = inputInfo;
2313
2314 try
2315 {
2316 reshapedInfo.SetShape(FlattenFullyConnectedInput(inputInfo.GetShape(), weights.GetInfo().GetShape()));
2317 } catch (const std::exception &e) {
2318 return Fail("%s: %s", __func__, e.what());
2319 }
2320
2321 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
2322 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
2323
2324 ActivationFn activationFunction;
2325 if (!GetInputActivationFunction<HalPolicy>(operation, 3, activationFunction, model, data))
2326 {
2327 return Fail("%s: Operation has invalid inputs", __func__);
2328 }
2329
2330 armnn::FullyConnectedDescriptor desc;
2331 desc.m_TransposeWeightMatrix = true;
2332 desc.m_BiasEnabled = true;
2333
2334 bool isSupported = false;
2335 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2336 IsFullyConnectedSupported,
2337 data.m_Backends,
2338 isSupported,
2339 reshapedInfo,
2340 outputInfo,
2341 weights.GetInfo(),
2342 bias.GetInfo(),
2343 desc);
2344 if (!isSupported)
2345 {
2346 return false;
2347 }
2348
2349 armnn::IConnectableLayer* startLayer =
2350 data.m_Network->AddFullyConnectedLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2351 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2352
2353 if (endLayer != nullptr)
2354 {
2355 if (inputInfo.GetNumDimensions() > 2U)
2356 {
2357 armnn::ReshapeDescriptor reshapeDescriptor;
2358 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
2359
2360 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
2361 assert(reshapeLayer != nullptr);
2362 input.Connect(reshapeLayer->GetInputSlot(0));
2363 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
2364 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
2365 }
2366 else
2367 {
2368 input.Connect(startLayer->GetInputSlot(0));
2369 }
2370
2371 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2372 }
2373 else
2374 {
2375 return Fail("%s: ProcessActivation failed", __func__);
2376 }
2377}
2378
2379template<typename HalPolicy,
2380 typename Operation = typename HalPolicy::Operation,
2381 typename Model = typename HalPolicy::Model>
2382bool ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
2383{
Mike Kelly999e2092019-08-15 10:46:46 +01002384 if (operation.inputs.size() != 1)
2385 {
2386 return Fail("%s: Optional inputs are not supported", __func__);
2387 }
2388
Mike Kelly46272802019-08-14 17:00:48 +01002389 using Operand = typename HalPolicy::Operand;
2390
2391 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2392 if (!input.IsValid())
2393 {
2394 return Fail("%s: Operation has invalid inputs", __func__);
2395 }
2396
2397 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2398 if (!output)
2399 {
2400 return Fail("%s: Could not read output 0", __func__);
2401 }
2402
2403 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2404 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2405
2406 if (IsDynamicTensor(outputInfo))
2407 {
2408 return Fail("%s: Dynamic output tensors are not supported", __func__);
2409 }
2410 if (outputInfo.GetNumDimensions() != 4u)
2411 {
2412 return Fail("%s: Tensor Rank other than 4 is not supported", __func__);
2413 }
2414
2415 armnn::L2NormalizationDescriptor desc;
2416 desc.m_DataLayout = armnn::DataLayout::NHWC;
2417
2418 bool isSupported = false;
2419 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2420 IsL2NormalizationSupported,
2421 data.m_Backends,
2422 isSupported,
2423 inputInfo,
2424 outputInfo,
2425 desc);
2426 if (!isSupported)
2427 {
2428 return false;
2429 }
2430
2431 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer(desc);
2432 assert(layer != nullptr);
2433 input.Connect(layer->GetInputSlot(0));
2434
2435 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2436}
2437
2438template<typename HalPolicy,
2439 typename Operation = typename HalPolicy::Operation,
2440 typename Model = typename HalPolicy::Model>
2441bool ConvertLocalResponseNormalization(const Operation& operation,
2442 const Model& model,
2443 ConversionData& data)
2444{
Mike Kelly999e2092019-08-15 10:46:46 +01002445 if (operation.inputs.size() != 5)
2446 {
2447 return Fail("%s: Optional inputs are not supported", __func__);
2448 }
2449
Mike Kelly46272802019-08-14 17:00:48 +01002450 using Operand = typename HalPolicy::Operand;
2451 using OperandType = typename HalPolicy::OperandType;
2452
2453 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2454 if (!input.IsValid())
2455 {
2456 return Fail("%s: Operation has invalid inputs", __func__);
2457 }
2458
2459 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2460 if (!output)
2461 {
2462 return Fail("%s: Could not read output 0", __func__);
2463 }
2464
2465 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2466 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2467
2468 if (IsDynamicTensor(outputInfo))
2469 {
2470 return Fail("%s: Dynamic output tensors are not supported", __func__);
2471 }
2472 if (outputInfo.GetNumDimensions() != 4u)
2473 {
2474 return Fail("%s: Tensor Rank other than 4 is not supported", __func__);
2475 }
2476
2477 armnn::NormalizationDescriptor descriptor;
2478 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
2479 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
2480 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
2481
2482 if (!input.IsValid() ||
2483 !GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) ||
2484 !GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_K, model, data) ||
2485 !GetInputFloat32<HalPolicy>(operation, 3, descriptor.m_Alpha, model, data) ||
2486 !GetInputFloat32<HalPolicy>(operation, 4, descriptor.m_Beta, model, data))
2487 {
2488 return Fail("%s: Operation has invalid inputs", __func__);
2489 }
2490
2491 // ArmNN expects normSize to be the full size of the normalization
2492 // window rather than the radius as in AndroidNN.
2493 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
2494
2495 bool isSupported = false;
2496 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2497 IsNormalizationSupported,
2498 data.m_Backends,
2499 isSupported,
2500 inputInfo,
2501 outputInfo,
2502 descriptor);
2503 if (!isSupported)
2504 {
2505 return false;
2506 }
2507
2508
2509 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
2510 assert(layer != nullptr);
2511 input.Connect(layer->GetInputSlot(0));
2512
2513 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2514}
2515
2516template<typename HalPolicy,
2517 typename Operation = typename HalPolicy::Operation,
2518 typename Model = typename HalPolicy::Model>
2519bool ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
2520{
2521 using Operand = typename HalPolicy::Operand;
2522
2523 armnn::ActivationDescriptor desc;
2524 desc.m_Function = armnn::ActivationFunction::Sigmoid;
2525
2526 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
2527}
2528
2529template<typename HalPolicy,
2530 typename Operation = typename HalPolicy::Operation,
2531 typename Model = typename HalPolicy::Model>
2532bool ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
2533{
2534 using Operand = typename HalPolicy::Operand;
2535
2536 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2537 if (!input.IsValid())
2538 {
2539 return Fail("%s: Operation has invalid inputs", __func__);
2540 }
2541
2542 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2543 if (!output)
2544 {
2545 return Fail("%s: Could not read output 0", __func__);
2546 }
2547
2548 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2549 if (IsDynamicTensor(outputInfo))
2550 {
2551 return Fail("%s: Dynamic output tensors are not supported", __func__);
2552 }
2553
2554 const Operand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2555 if (!axisOperand)
2556 {
2557 return Fail("%s: Could not read input 1", __func__);
2558 }
2559
2560 std::vector<int32_t> axis;
2561 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2562 {
2563 return Fail("%s: Input 1 has invalid values", __func__);
2564 }
2565
2566 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2567
2568 // Convert the axis to unsigned int and remove duplicates.
2569 unsigned int rank = inputInfo.GetNumDimensions();
2570 std::set<unsigned int> uniqueAxis;
2571 std::transform(axis.begin(), axis.end(),
2572 std::inserter(uniqueAxis, uniqueAxis.begin()),
2573 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2574
2575 // Get the "keep dims" flag.
2576 int32_t keepDims = 0;
2577 if (!GetInputInt32<HalPolicy>(operation, 2, keepDims, model, data))
2578 {
2579 return Fail("%s: Could not read input 2", __func__);
2580 }
2581
2582 armnn::MeanDescriptor descriptor;
2583 descriptor.m_Axis.assign(uniqueAxis.begin(), uniqueAxis.end());
2584 descriptor.m_KeepDims = keepDims > 0;
2585
2586 bool isSupported = false;
2587 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2588 IsMeanSupported,
2589 data.m_Backends,
2590 isSupported,
2591 inputInfo,
2592 outputInfo,
2593 descriptor);
2594 if (!isSupported)
2595 {
2596 return false;
2597 }
2598
2599 armnn::IConnectableLayer* const layer = data.m_Network->AddMeanLayer(descriptor);
2600 assert(layer != nullptr);
2601 input.Connect(layer->GetInputSlot(0));
2602
2603 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2604}
2605
2606template<typename HalPolicy,
2607 typename Operation = typename HalPolicy::Operation,
2608 typename Model = typename HalPolicy::Model>
2609bool ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
2610{
2611 using Operand = typename HalPolicy::Operand;
2612
2613 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2614 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
2615
2616 if (!input0.IsValid() || !input1.IsValid())
2617 {
2618 return Fail("%s: Operation has invalid inputs", __func__);
2619 }
2620
2621 // The FuseActivation parameter is always the input index 2
2622 // and it should be optional
2623 ActivationFn activationFunction;
2624 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2625 {
2626 return Fail("%s: Operation has invalid inputs", __func__);
2627 }
2628
2629 const Operand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2630
2631 if (outputOperand == nullptr)
2632 {
2633 return false;
2634 }
2635
2636 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2637 if (IsDynamicTensor(outputInfo))
2638 {
2639 return Fail("%s: Dynamic output tensors are not supported", __func__);
2640 }
2641
2642 bool isSupported = false;
2643 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2644 IsMultiplicationSupported,
2645 data.m_Backends,
2646 isSupported,
2647 input0.GetTensorInfo(),
2648 input1.GetTensorInfo(),
2649 outputInfo);
2650 if (!isSupported)
2651 {
2652 return false;
2653 }
2654
2655 armnn::IConnectableLayer* const startLayer = data.m_Network->AddMultiplicationLayer();
2656 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2657
2658 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
2659 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
2660
2661 if (endLayer != nullptr)
2662 {
Sadik Armagan64b19b52019-08-19 09:49:58 +01002663 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
2664 if (!isReshapeSupported)
2665 {
2666 return false;
2667 }
2668
Mike Kelly46272802019-08-14 17:00:48 +01002669 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2670 }
2671 else
2672 {
2673 return Fail("%s: ProcessActivation failed", __func__);
2674 }
2675}
2676
2677template<typename HalPolicy,
2678 typename Operation = typename HalPolicy::Operation,
2679 typename Model = typename HalPolicy::Model>
2680bool ConvertPad(Operation& operation, const Model& model, ConversionData& data)
2681{
2682 using Operand = typename HalPolicy::Operand;
2683
Mike Kelly3c673942019-07-25 09:26:06 +01002684 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2685 if (!input.IsValid())
2686 {
2687 return Fail("%s: Operation has invalid inputs", __func__);
2688 }
2689
2690 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2691 unsigned int rank = inputInfo.GetNumDimensions();
2692
2693 armnn::PadDescriptor descriptor;
2694 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
2695 {
2696 return Fail("%s: Could not convert paddings", __func__);
2697 }
2698
2699 // Before Android Q, the pad value for ANEURALNETWORKS_TENSOR_QUANT8_ASYMM was undefined. Since Android Q the pad
2700 // value must be "logical zero" we set it to be equal to the QuantizationOffset so effectively it ends up as
2701 // (QuantizationOffset - QuantizationOffset) * scale = 0.
2702 if (inputInfo.GetDataType() == armnn::DataType::QuantisedAsymm8)
2703 {
2704 descriptor.m_PadValue = inputInfo.GetQuantizationOffset();
2705 }
2706
Mike Kelly46272802019-08-14 17:00:48 +01002707 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly3c673942019-07-25 09:26:06 +01002708 if (!output)
2709 {
2710 return Fail("%s: Could not read output", __func__);
2711 }
2712
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002713 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kelly3c673942019-07-25 09:26:06 +01002714 if (IsDynamicTensor(outputInfo))
2715 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002716 return Fail("%s: Dynamic output tensors are not supported", __func__);
Mike Kelly3c673942019-07-25 09:26:06 +01002717 }
2718
2719 bool isSupported = false;
2720 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2721 IsPadSupported,
2722 data.m_Backends,
2723 isSupported,
2724 inputInfo,
2725 outputInfo,
2726 descriptor);
2727 if (!isSupported)
2728 {
2729 return false;
2730 }
2731
2732 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
2733 assert(layer != nullptr);
2734 input.Connect(layer->GetInputSlot(0));
2735 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
2736
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002737 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +01002738}
2739
Mike Kelly0a879362019-07-29 16:56:31 +01002740template<typename HalPolicy,
2741 typename Operation = typename HalPolicy::Operation,
Mike Kelly46272802019-08-14 17:00:48 +01002742 typename Model = typename HalPolicy::Model>
2743bool ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
2744{
2745 using Operand = typename HalPolicy::Operand;
2746
2747 const Operand* inputOperand = GetInputOperand<HalPolicy>(operation, 0, model);
2748 const Operand* requestedShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2749 const Operand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2750
2751 if (inputOperand == nullptr
2752 || requestedShapeOperand == nullptr
2753 || outputOperand == nullptr)
2754 {
2755 return Fail("%s: Operation has invalid inputs", __func__);
2756 }
2757
2758 if (requestedShapeOperand->dimensions.size() != 1)
2759 {
2760 return Fail("%s: Input 1 expected to be one-dimensional (found %i dimensions)",
2761 __func__, requestedShapeOperand->dimensions.size());
2762 }
2763
2764 std::vector<int32_t> targetDimensions;
2765 if (!GetTensorInt32Values<HalPolicy>(*requestedShapeOperand, targetDimensions, model, data))
2766 {
2767 return Fail("%s: Could not read values of input 1", __func__);
2768 }
2769
2770 const Shape inputOperandShape = GetOperandShape(*inputOperand);
2771
2772 Shape requestedShape;
2773 // targetDimensions may contain special values (e.g. -1). reshapePrepare() is an AndroidNN provided utility
2774 // function that resolves these values into a fully specified tensor shape.
2775 if (!reshapePrepare(inputOperandShape, targetDimensions.data(), targetDimensions.size(), &requestedShape))
2776 {
2777 return Fail("%s: Failed to resolve the requested shape", __func__);
2778 }
2779
2780 const Shape outputOperandShape = GetOperandShape(*outputOperand);
2781 if (!SameShape(requestedShape, outputOperandShape))
2782 {
2783 return Fail("%s: Shape of output operand does not match resolved requested shape", __func__);
2784 }
2785
2786 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2787 if (!input.IsValid())
2788 {
2789 return Fail("%s: Could not read input 0", __func__);
2790 }
2791
2792 armnn::ReshapeDescriptor reshapeDescriptor;
2793 reshapeDescriptor.m_TargetShape = armnn::TensorShape(requestedShape.dimensions.size(),
2794 requestedShape.dimensions.data());
2795
2796 bool isSupported = false;
2797 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2798 IsReshapeSupported,
2799 data.m_Backends,
2800 isSupported,
2801 input.GetTensorInfo(),
2802 reshapeDescriptor);
2803 if (!isSupported)
2804 {
2805 return false;
2806 }
2807
2808 armnn::IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
2809 assert(layer != nullptr);
2810 input.Connect(layer->GetInputSlot(0));
2811
2812 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2813}
2814
2815template<typename HalPolicy,
2816 typename Operation = typename HalPolicy::Operation,
Mike Kelly0a879362019-07-29 16:56:31 +01002817 typename Model = typename HalPolicy::Model>
2818bool ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
2819{
Mike Kelly46272802019-08-14 17:00:48 +01002820 using Operand = typename HalPolicy::Operand;
2821
Mike Kelly0a879362019-07-29 16:56:31 +01002822 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2823 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
2824
2825 if (!input0.IsValid() || !input1.IsValid())
2826 {
2827 return Fail("%s: Operation has invalid inputs", __func__);
2828 }
2829
2830 // The FuseActivation parameter is always the input index 2
2831 // and it should be optional
2832 ActivationFn activationFunction;
2833 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2834 {
2835 return Fail("%s: Operation has invalid inputs", __func__);
2836 }
2837
2838 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2839 if (!output)
2840 {
2841 return Fail("%s: Could not read output 0", __func__);
2842 }
2843
2844 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2845 if (IsDynamicTensor(outputInfo))
2846 {
2847 return Fail("%s: Dynamic output tensors are not supported", __func__);
2848 }
2849
2850 bool isSupported = false;
2851 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2852 IsSubtractionSupported,
2853 data.m_Backends,
2854 isSupported,
2855 input0.GetTensorInfo(),
2856 input1.GetTensorInfo(),
2857 outputInfo);
2858 if (!isSupported)
2859 {
2860 return false;
2861 }
2862
2863 armnn::IConnectableLayer* const startLayer = data.m_Network->AddSubtractionLayer();
2864 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2865
2866 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
2867 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
2868
2869 if (endLayer)
2870 {
Sadik Armagan64b19b52019-08-19 09:49:58 +01002871 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
2872 if (!isReshapeSupported)
2873 {
2874 return false;
2875 }
Mike Kelly0a879362019-07-29 16:56:31 +01002876 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2877 }
2878
2879 return Fail("%s: ProcessActivation failed", __func__);
2880}
2881
Finn Williams23b87b32019-07-30 11:44:05 +01002882template<typename HalPolicy,
Mike Kelly46272802019-08-14 17:00:48 +01002883 typename Operation = typename HalPolicy::Operation,
2884 typename Model = typename HalPolicy::Model>
2885bool ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
2886{
2887 using Operand = typename HalPolicy::Operand;
2888
2889 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2890 if (!input.IsValid())
2891 {
2892 return Fail("%s: Operation has invalid inputs", __func__);
2893 }
2894
2895 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2896 unsigned int rank = inputInfo.GetNumDimensions();
2897 if (rank > 4)
2898 {
2899 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
2900 }
2901
2902 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2903 if (!output)
2904 {
2905 return Fail("%s: Could not read output 0", __func__);
2906 }
2907
2908 if (IsDynamicTensor(GetTensorInfoForOperand(*output)))
2909 {
2910 return Fail("%s: Dynamic output tensors are not supported", __func__);
2911 }
2912
2913 // NOTE: Axis is an optional parameter to SQUEEZE, therefore we do not want to generate a failure
2914 // if the operand index is out of bounds.
2915 const Operand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model, false);
2916
2917 const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
2918
2919 std::vector<int32_t> axis;
2920 if (!axisOperand)
2921 {
2922 axis.assign(dimensionSequence,
2923 dimensionSequence + rank);
2924 }
2925 else
2926 {
2927 GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data);
2928 }
2929
2930 std::vector<uint32_t> outputDims;
2931 for (unsigned int i = 0; i < rank; i++)
2932 {
2933 bool skipSqueeze = (std::find(axis.begin(), axis.end(), i) == axis.end());
2934 auto currentDimension = inputInfo.GetShape()[i];
2935 if (skipSqueeze || currentDimension != 1)
2936 {
2937 outputDims.push_back(currentDimension);
2938 }
2939 }
2940
2941 armnn::TensorShape outShape = armnn::TensorShape(outputDims.size(), outputDims.data());
2942
2943 armnn::TensorInfo outputInfo = inputInfo;
2944 outputInfo.SetShape(outShape);
2945
2946 armnn::ReshapeDescriptor reshapeDesc;
2947 reshapeDesc.m_TargetShape = outputInfo.GetShape();
2948
2949 bool isSupported = false;
2950 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2951 IsReshapeSupported,
2952 data.m_Backends,
2953 isSupported,
2954 inputInfo,
2955 reshapeDesc);
2956 if (!isSupported)
2957 {
2958 return false;
2959 }
2960
2961 armnn::IConnectableLayer* const layer = data.m_Network->AddReshapeLayer(reshapeDesc);
2962 assert(layer != nullptr);
2963 input.Connect(layer->GetInputSlot(0));
2964
2965 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2966}
2967
2968template<typename HalPolicy,
2969 typename Operation = typename HalPolicy::Operation,
2970 typename Model = typename HalPolicy::Model>
2971bool ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
2972{
2973 using Operand = typename HalPolicy::Operand;
2974
2975 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2976 if (!input.IsValid())
2977 {
2978 return Fail("%s: Operation has invalid inputs", __func__);
2979 }
2980
2981 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2982 unsigned int rank = inputInfo.GetNumDimensions();
2983 if (rank > 4)
2984 {
2985 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
2986 }
2987
2988 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2989 if (!output)
2990 {
2991 return Fail("%s: Could not read output 0", __func__);
2992 }
2993
2994 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2995 if (IsDynamicTensor(outputInfo))
2996 {
2997 return Fail("%s: Dynamic output tensors are not supported", __func__);
2998 }
2999
3000 const Operand* beginOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3001 const Operand* endOperand = GetInputOperand<HalPolicy>(operation, 2, model);
3002 const Operand* stridesOperand = GetInputOperand<HalPolicy>(operation, 3, model);
3003
3004 std::vector<int32_t> beginValues;
3005 std::vector<int32_t> endValues;
3006 std::vector<int32_t> stridesValues;
3007
3008 // The length of the beginOperand, endOperand and stridesOperand must be of a rank(input)
3009 auto ValidateInputOperands = [&] (const Operand& operand, std::vector<int32_t>& operandValues)
3010 {
3011 if (!GetTensorInt32Values<HalPolicy>(operand, operandValues, model, data))
3012 {
3013 return false;
3014 }
3015
3016 if (operandValues.size() != rank)
3017 {
3018 return false;
3019 }
3020
3021 return true;
3022 };
3023
3024 if (!ValidateInputOperands(*beginOperand, beginValues)
3025 || !ValidateInputOperands(*endOperand, endValues)
3026 || !ValidateInputOperands(*stridesOperand, stridesValues))
3027 {
3028 return Fail("%s: Operation has invalid input operand", __func__);
3029 }
3030
3031 // Stride cannot have value '0'
3032 if (std::any_of(stridesValues.cbegin(), stridesValues.cend(), [](int32_t i){ return i == 0; }))
3033 {
3034 return Fail("%s: Stride must be non-zero value.", __func__);
3035 }
3036
3037 armnn::StridedSliceDescriptor descriptor;
3038 descriptor.m_Begin.assign(beginValues.cbegin(), beginValues.cend());
3039 descriptor.m_End.assign(endValues.cbegin(), endValues.cend());
3040 descriptor.m_Stride.assign(stridesValues.cbegin(), stridesValues.cend());
3041 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
3042
3043 // Get the "begin_mask", "end_mask", and "shrink_axis_mask" flags
3044 if (!GetInputInt32<HalPolicy>(operation, 4, descriptor.m_BeginMask, model, data) ||
3045 !GetInputInt32<HalPolicy>(operation, 5, descriptor.m_EndMask, model, data) ||
3046 !GetInputInt32<HalPolicy>(operation, 6, descriptor.m_ShrinkAxisMask, model, data))
3047 {
3048 return Fail("%s: Operation has invalid inputs", __func__);
3049 }
3050
3051 bool isSupported = false;
3052 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3053 IsStridedSliceSupported,
3054 data.m_Backends,
3055 isSupported,
3056 inputInfo,
3057 outputInfo,
3058 descriptor);
3059 if (!isSupported)
3060 {
3061 return false;
3062 }
3063
3064 armnn::IConnectableLayer* const layer = data.m_Network->AddStridedSliceLayer(descriptor);
3065 assert(layer != nullptr);
3066 input.Connect(layer->GetInputSlot(0));
3067
3068 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3069}
3070
3071template<typename HalPolicy,
3072 typename Operation = typename HalPolicy::Operation,
3073 typename Model = typename HalPolicy::Model>
3074bool ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
3075{
3076 using Operand = typename HalPolicy::Operand;
3077
3078 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3079 if (!input.IsValid())
3080 {
3081 return Fail("%s: Operation has invalid inputs", __func__);
3082 }
3083
3084 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3085 unsigned int rank = inputInfo.GetNumDimensions();
3086 if (rank > 4)
3087 {
3088 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3089 }
3090
3091 // NOTE: Axis is an optional parameter to TRANSPOSE, therefore we do not want to generate a failure
3092 // if the operand index is out of bounds.
3093 const Operand* permOperand = GetInputOperand<HalPolicy>(operation, 1, model, false);
3094
3095 std::vector<int32_t> perm(rank);
3096 if (!permOperand)
3097 {
3098 // NOTE: If perm is not given, it is set to (n-1...0), where n is the rank of the tensor
3099 for (unsigned int i = rank; i > 0; i--)
3100 {
3101 perm[rank - i] = boost::numeric_cast<int> (i - 1);
3102 }
3103 }
3104 else
3105 {
3106 GetTensorInt32Values<HalPolicy>(*permOperand, perm, model, data);
3107 }
3108
3109 std::vector<uint32_t> outputDims(perm.begin(), perm.begin() + rank);
3110
3111 auto permutationVector = armnn::PermutationVector(outputDims.data(), outputDims.size());
3112 if (!permutationVector.IsEqual(NHWCToArmNN)
3113 && !permutationVector.IsEqual(ArmNNToNHWC)
3114 && !permutationVector.IsEqual({ 3, 2, 0, 1 }))
3115 {
3116 return Fail("%s: Only [0, 3, 1, 2], [0, 2, 3, 1] and [3, 2, 0, 1] permutations are supported.", __func__);
3117 }
3118
3119 armnn::PermuteDescriptor permuteDesc;
3120 permuteDesc.m_DimMappings = permutationVector;
3121
3122 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3123 if (!output)
3124 {
3125 return Fail("%s: Could not read output 0", __func__);
3126 }
3127
3128 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3129
3130 bool isSupported = false;
3131 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3132 IsPermuteSupported,
3133 data.m_Backends,
3134 isSupported,
3135 inputInfo,
3136 outputInfo,
3137 permuteDesc);
3138 if (!isSupported)
3139 {
3140 return false;
3141 }
3142
3143 armnn::IConnectableLayer* const layer = data.m_Network->AddPermuteLayer(permuteDesc);
3144 assert(layer != nullptr);
3145 input.Connect(layer->GetInputSlot(0));
3146
3147 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3148}
3149
3150template<typename HalPolicy,
Finn Williams23b87b32019-07-30 11:44:05 +01003151 typename HalOperation = typename HalPolicy::Operation,
Finn Williams0e4e4392019-07-31 10:56:27 +01003152 typename HalOperand = typename HalPolicy::Operand,
Finn Williams23b87b32019-07-30 11:44:05 +01003153 typename HalModel = typename HalPolicy::Model>
3154bool ConvertBatchToSpaceNd(const HalOperation& operation,
3155 const HalModel& model,
3156 ConversionData& data)
3157{
Finn Williams23b87b32019-07-30 11:44:05 +01003158
3159 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3160 if (!input.IsValid())
3161 {
3162 return Fail("%s: Operation has invalid inputs", __func__);
3163 }
3164
3165 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3166 if (!output)
3167 {
3168 return Fail("%s: Could not read output 0", __func__);
3169 }
3170
3171 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3172 if (IsDynamicTensor(outputInfo))
3173 {
3174 return Fail("%s: Dynamic output tensors are not supported", __func__);
3175 }
3176
3177 const HalOperand* blockOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3178 if (!blockOperand)
3179 {
3180 return Fail("%s: Could not read input 1", __func__);
3181 }
3182
3183 // Convert the block operand to int32
3184 std::vector<int32_t> block;
3185 if (!GetTensorInt32Values<HalPolicy>(*blockOperand, block, model, data))
3186 {
3187 return Fail("%s: Input 1 has invalid values", __func__);
3188 }
3189
3190 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3191
3192 unsigned int rank = inputInfo.GetNumDimensions();
3193 if (rank != 4)
3194 {
3195 Fail("%s: Only inputs with rank equal to 4 are supported", __func__);
3196 }
3197
3198 if (std::any_of(block.cbegin(), block.cend(), [](int32_t i){ return i < 1; }))
3199 {
3200 return Fail("%s: Block sizes for each spatial dimension of the input tensor must be"
3201 " greater than or equal to 1", __func__);
3202 }
3203
3204 armnn::BatchToSpaceNdDescriptor batchToSpaceNdDesc;
3205 batchToSpaceNdDesc.m_BlockShape.assign(block.cbegin(), block.cend());
3206 batchToSpaceNdDesc.m_DataLayout = armnn::DataLayout::NHWC;
3207
3208 if (Is12Operand(*output))
3209 {
Finn Williams0e4e4392019-07-31 10:56:27 +01003210 batchToSpaceNdDesc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +01003211 }
3212 // Setting crops to 0,0 0,0 as it is not supported in Android NN API
3213 batchToSpaceNdDesc.m_Crops = {{0, 0}, {0, 0}};
3214
3215 bool isSupported = false;
3216 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3217 IsBatchToSpaceNdSupported,
3218 data.m_Backends,
3219 isSupported,
3220 inputInfo,
3221 outputInfo,
3222 batchToSpaceNdDesc);
3223 if (!isSupported)
3224 {
3225 return false;
3226 }
3227
3228 armnn::IConnectableLayer* const layer = data.m_Network->AddBatchToSpaceNdLayer(batchToSpaceNdDesc);
3229 assert(layer != nullptr);
3230 input.Connect(layer->GetInputSlot(0));
3231
3232 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3233}
Mike Kelly0a879362019-07-29 16:56:31 +01003234
Finn Williamsd74c5052019-07-30 17:06:00 +01003235template<typename HalPolicy,
3236 typename HalOperation = typename HalPolicy::Operation,
3237 typename HalOperand = typename HalPolicy::Operand,
3238 typename HalModel = typename HalPolicy::Model>
3239bool ConvertSpaceToBatchNd(const HalOperation& operation, const HalModel& model, ConversionData& data)
3240{
3241 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3242 if (!input.IsValid())
3243 {
3244 return Fail("%s: Operation has invalid inputs", __func__);
3245 }
3246
3247 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3248 unsigned int rank = inputInfo.GetNumDimensions();
3249 unsigned int spatialDim = rank - 2;
3250
3251 if (rank != 4)
3252 {
3253 Fail("%s: Only inputs with rank 4 are supported", __func__);
3254 }
3255
3256 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3257 if (!output)
3258 {
3259 return Fail("%s: Could not read output 0", __func__);
3260 }
3261
3262 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3263 if (IsDynamicTensor(outputInfo))
3264 {
3265 return Fail("%s: Dynamic output tensors are not supported", __func__);
3266 }
3267
3268 const HalOperand* blockShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3269 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 2, model);
3270
3271 armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand(*blockShapeOperand);
3272 if (blockShapeOperandShape.GetNumDimensions() != 1 || blockShapeOperandShape.GetNumElements() != spatialDim)
3273 {
3274 return Fail("%s: Operation has invalid block shape operand: expected shape [%d]", __func__, spatialDim);
3275 }
3276
3277 std::vector<int32_t> blockShape;
3278 GetTensorInt32Values<HalPolicy>(*blockShapeOperand, blockShape, model, data);
3279 if (std::any_of(blockShape.cbegin(), blockShape.cend(), [](int32_t i){ return i < 1; }))
3280 {
3281 return Fail("%s: Block shape must be at least 1 in all dimensions.", __func__);
3282 }
3283
3284 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
3285 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != 2 * spatialDim)
3286 {
3287 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, spatialDim);
3288 }
3289
3290 std::vector<std::pair<unsigned int, unsigned int>> paddingList;
3291 std::vector<int32_t> paddings;
3292 GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data);
3293 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
3294 {
3295 int paddingBeforeInput = paddings[i];
3296 int paddingAfterInput = paddings[i + 1];
3297 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
3298 {
3299 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
3300 }
3301
3302 paddingList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
3303 }
3304
3305 armnn::SpaceToBatchNdDescriptor descriptor;
3306 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
3307 descriptor.m_BlockShape.assign(blockShape.cbegin(), blockShape.cend());
3308 descriptor.m_PadList.assign(paddingList.cbegin(), paddingList.cend());
3309
3310 if (Is12Operand(*output))
3311 {
3312 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
3313 }
3314
3315 bool isSupported = false;
3316 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3317 IsSpaceToBatchNdSupported,
3318 data.m_Backends,
3319 isSupported,
3320 inputInfo,
3321 outputInfo,
3322 descriptor);
3323 if (!isSupported)
3324 {
3325 return false;
3326 }
3327
3328 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToBatchNdLayer(descriptor);
3329 assert(layer != nullptr);
3330 input.Connect(layer->GetInputSlot(0));
3331
3332 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3333}
3334
Kevin May407718f2019-09-09 14:46:41 +01003335template<typename HalPolicy,
3336 typename HalOperation = typename HalPolicy::Operation,
3337 typename HalModel = typename HalPolicy::Model>
3338bool ConvertAbs(const HalOperation& operation, const HalModel& model, ConversionData& data)
3339{
3340 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3341
3342 if (!input.IsValid())
3343 {
3344 return Fail("%s: Operation has invalid input", __func__);
3345 }
3346
3347 using HalOperand = typename HalPolicy::Operand;
3348 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3349 if (!output)
3350 {
3351 return Fail("%s: Could not read output 0", __func__);
3352 }
3353
3354 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3355 if (IsDynamicTensor(outputInfo))
3356 {
3357 return Fail("%s: Dynamic output tensors are not supported", __func__);
3358 }
3359
3360 bool isSupported = false;
3361 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3362 IsAbsSupported,
3363 data.m_Backends,
3364 isSupported,
3365 input.GetTensorInfo(),
3366 outputInfo);
3367
3368 if (!isSupported)
3369 {
3370 return false;
3371 }
3372
3373 armnn::IConnectableLayer* const layer = data.m_Network->AddAbsLayer();
3374 assert(layer != nullptr);
3375 input.Connect(layer->GetInputSlot(0));
3376
3377 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3378}
3379
3380
saoste01b8471482018-10-10 09:44:51 +01003381} // namespace armnn_driver