blob: bcccd272c91dd5721c83da8e9e83934a48c5ef15 [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 ||
Pablo Tellofb45e2f2019-10-18 16:51:57 +0100190 type == V1_2::OperandType::TENSOR_QUANT8_SYMM ||
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000191 type == V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
192 type == V1_2::OperandType::TENSOR_QUANT16_SYMM ||
Mike Kellyb5fdf382019-06-11 16:35:25 +0100193 type == V1_2::OperandType::TENSOR_INT32;
194}
195
196#endif
197
198inline bool IsBool(V1_0::Operand)
199{
200 return false;
201}
202
Sadik Armagan61113162019-07-25 09:09:40 +0100203inline bool Is12Operand(V1_0::Operand)
204{
205 return false;
206}
207
Mike Kellyb5fdf382019-06-11 16:35:25 +0100208#ifdef ARMNN_ANDROID_NN_V1_2
209
210inline bool IsBool(V1_2::Operand operand)
211{
212 return operand.type == V1_2::OperandType::BOOL;
213}
214
Sadik Armagan61113162019-07-25 09:09:40 +0100215/// Checks if a operand is 1_2 Operand
216inline bool Is12Operand(V1_2::Operand)
217{
218 return true;
219}
220
Mike Kellyb5fdf382019-06-11 16:35:25 +0100221#endif
222
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100223template<typename LayerHandleType>
224armnn::IConnectableLayer& AddReshapeLayer(armnn::INetwork& network, LayerHandleType& inputLayer,
225 armnn::TensorInfo reshapeInfo)
226{
227 armnn::ReshapeDescriptor reshapeDescriptor;
228 reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
229
230 armnn::IConnectableLayer* reshapeLayer = network.AddReshapeLayer(reshapeDescriptor);
231 BOOST_ASSERT(reshapeLayer != nullptr);
232
233 // Attach the input layer to the reshape layer
234 inputLayer.Connect(reshapeLayer->GetInputSlot(0));
235 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapeInfo);
236
237 return *reshapeLayer;
238}
239
Sadik Armagan64b19b52019-08-19 09:49:58 +0100240bool BroadcastTensor(LayerInputHandle& input0, LayerInputHandle& input1,
241 armnn::IConnectableLayer* startLayer, ConversionData& data)
arovir01b0717b52018-09-05 17:03:25 +0100242{
243 BOOST_ASSERT(startLayer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +0100244
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100245 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
246 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
247
248 unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
249 unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
250
251 if (inputDimensions0 == inputDimensions1)
arovir01b0717b52018-09-05 17:03:25 +0100252 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100253 // The inputs have the same number of dimensions, simply connect them to the given layer as they are
254 input0.Connect(startLayer->GetInputSlot(0));
255 input1.Connect(startLayer->GetInputSlot(1));
256
Sadik Armagan64b19b52019-08-19 09:49:58 +0100257 return true;
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100258 }
259
260 // Since the number of dimensions do not match then we need to add degenerate dimensions
261 // to the "smaller" tensor using a reshape, while keeping the order of the inputs.
262
263 unsigned int maxInputDimensions = std::max(inputDimensions0, inputDimensions1);
264 unsigned int sizeDifference = std::abs(boost::numeric_cast<int>(inputDimensions0) -
265 boost::numeric_cast<int>(inputDimensions1));
266
267 bool input0IsSmaller = inputDimensions0 < inputDimensions1;
268 LayerInputHandle& smallInputHandle = input0IsSmaller ? input0 : input1;
269 const armnn::TensorInfo& smallInfo = smallInputHandle.GetTensorInfo();
270
271 const armnn::TensorShape& smallShape = smallInfo.GetShape();
272 std::vector<unsigned int> reshapedDimensions(maxInputDimensions, 1);
273 for (unsigned int i = sizeDifference; i < maxInputDimensions; i++)
274 {
275 reshapedDimensions[i] = smallShape[i - sizeDifference];
276 }
277
278 armnn::TensorInfo reshapedInfo = smallInfo;
279 reshapedInfo.SetShape(armnn::TensorShape{ boost::numeric_cast<unsigned int>(reshapedDimensions.size()),
280 reshapedDimensions.data() });
Sadik Armagan64b19b52019-08-19 09:49:58 +0100281
282 // RehsapeDescriptor that is ignored in the IsReshapeSupported function
283 armnn::ReshapeDescriptor reshapeDescriptor;
284
285 bool isSupported = false;
286 FORWARD_LAYER_SUPPORT_FUNC(__func__,
287 IsReshapeSupported,
288 data.m_Backends,
289 isSupported,
290 reshapedInfo,
291 reshapeDescriptor);
292 if (!isSupported)
293 {
294 return false;
295 }
296
297 BOOST_ASSERT(data.m_Network != nullptr);
298 armnn::IConnectableLayer& reshapeLayer = AddReshapeLayer(*data.m_Network, smallInputHandle, reshapedInfo);
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100299
300 if (input0IsSmaller)
301 {
302 // Input0 is the "smaller" tensor, connect the reshape layer as follows:
303 //
304 // Input0 Input1
arovir01b0717b52018-09-05 17:03:25 +0100305 // | |
306 // Reshape |
307 // \ /
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100308 // StartLayer
arovir01b0717b52018-09-05 17:03:25 +0100309
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100310 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
311 input1.Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100312 }
313 else
314 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100315 // Input1 is the "smaller" tensor, connect the reshape layer as follows:
316 //
317 // Input0 Input1
318 // | |
319 // | Reshape
320 // \ /
321 // StartLayer
322
arovir01b0717b52018-09-05 17:03:25 +0100323 input0.Connect(startLayer->GetInputSlot(0));
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100324 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100325 }
Sadik Armagan64b19b52019-08-19 09:49:58 +0100326
327 return true;
arovir01b0717b52018-09-05 17:03:25 +0100328}
329
330void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t& outPadHead, uint32_t& outPadTail,
331 android::nn::PaddingScheme scheme)
332{
333 int32_t padHead;
334 int32_t padTail;
335 calculateExplicitPadding(input, stride, kernel, scheme, &padHead, &padTail);
336 outPadHead = boost::numeric_cast<uint32_t>(padHead);
337 outPadTail = boost::numeric_cast<uint32_t>(padTail);
338}
339
Mike Kelly86b36d42019-07-12 16:39:33 +0100340#ifdef ARMNN_ANDROID_NN_V1_2
341
342void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t dilation, uint32_t& outPadHead,
343 uint32_t& outPadTail, android::nn::PaddingScheme scheme)
344{
345 int32_t padHead;
346 int32_t padTail;
347 calculateExplicitPadding(input, stride, dilation, kernel, scheme, &padHead, &padTail);
348 outPadHead = boost::numeric_cast<uint32_t>(padHead);
349 outPadTail = boost::numeric_cast<uint32_t>(padTail);
350}
351
Narumol Prangnawaratc8bdb392019-08-01 15:51:44 +0100352void CalcPaddingTransposeConv(uint32_t output, uint32_t kernel, uint32_t stride, int32_t& outPadHead,
353 int32_t& outPadTail, android::nn::PaddingScheme scheme)
354{
355 calculateExplicitPaddingTransposeConv(output, stride, kernel, scheme, &outPadHead, &outPadTail);
356}
357
Mike Kelly86b36d42019-07-12 16:39:33 +0100358#endif
359
Matthew Bentham912b3622019-05-03 15:49:14 +0100360Shape GetOperandShape(const V1_0::Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100361{
362 Shape shape;
Matthew Bentham912b3622019-05-03 15:49:14 +0100363 shape.type = OperandType(operand.type);
arovir01b0717b52018-09-05 17:03:25 +0100364 shape.dimensions = operand.dimensions;
365 shape.scale = operand.scale;
366 shape.offset = operand.zeroPoint;
367 return shape;
368}
369
Mike Kelly46272802019-08-14 17:00:48 +0100370#ifdef ARMNN_ANDROID_NN_V1_2
371
372Shape GetOperandShape(const V1_2::Operand& operand)
373{
374 Shape shape;
375 shape.type = OperandType(operand.type);
376 shape.dimensions = operand.dimensions;
377 shape.scale = operand.scale;
378 shape.offset = operand.zeroPoint;
379 return shape;
380}
381
382#endif
383
arovir01b0717b52018-09-05 17:03:25 +0100384// ArmNN requires the bias scale to be equal to the product of the weight and input scales, which is also
385// 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 +0100386// we accept some tolerance. We don't want ArmNN itself to accept these inconsistencies as it is up to the
387// user (us, in this case) to ensure they match.
arovir01b0717b52018-09-05 17:03:25 +0100388void SanitizeBiasQuantizationScale(armnn::TensorInfo& biasInfo,
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000389 const armnn::TensorInfo& weightInfo,
390 const armnn::TensorInfo& inputInfo)
arovir01b0717b52018-09-05 17:03:25 +0100391{
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000392 if (weightInfo.HasPerAxisQuantization())
arovir01b0717b52018-09-05 17:03:25 +0100393 {
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000394 // NOTE: Bias scale is always set to 0 for per-axis quantization and
395 // it needs to be calculated: scale[i] = input_scale * weight_scale[i]
396 auto UpdateBiasScaleValue = [&inputInfo](float biasScale) -> float
arovir01b0717b52018-09-05 17:03:25 +0100397 {
Aron Virginas-Tar9f0693b2019-11-06 14:32:30 +0000398 return biasScale * inputInfo.GetQuantizationScale();
399 };
400
401 std::vector<float> biasScales(weightInfo.GetQuantizationScales());
402 std::transform(biasScales.begin(), biasScales.end(), biasScales.begin(), UpdateBiasScaleValue);
403
404 biasInfo.SetQuantizationScales(biasScales);
405 biasInfo.SetQuantizationDim(weightInfo.GetQuantizationDim());
406
407 ALOGV("Bias quantization params have been updated for per-axis quantization");
408 }
409 else
410 {
411 const float expectedBiasScale = weightInfo.GetQuantizationScale() * inputInfo.GetQuantizationScale();
412 if (biasInfo.GetQuantizationScale() != expectedBiasScale)
413 {
414 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
415 if (comparer(biasInfo.GetQuantizationScale(), expectedBiasScale))
416 {
417 ALOGW("Bias quantization scale has been modified to match input * weights");
418 biasInfo.SetQuantizationScale(expectedBiasScale);
419 }
arovir01b0717b52018-09-05 17:03:25 +0100420 }
421 }
422}
423
424// 4D Tensor Permutations
425const armnn::PermutationVector IdentityPermutation4D({ 0U, 1U, 2U, 3U });
426const armnn::PermutationVector NHWCToArmNN({ 0U, 2U, 3U, 1U });
427const armnn::PermutationVector ArmNNToNHWC({ 0U, 3U, 1U, 2U });
428const armnn::PermutationVector SwapDim1And2({ 0U, 2U, 1U, 3U });
429
430// 3D Permutation Vectors
431const armnn::PermutationVector IdentityPermutation3D({ 0U, 1U, 2U });
432const armnn::PermutationVector RotateTensorLeft({ 2U, 0U, 1U });
433const armnn::PermutationVector RotateTensorRight({ 1U, 2U, 0U });
434
435template<typename OSlot>
436armnn::IConnectableLayer& AddPermuteLayer(armnn::INetwork& network, OSlot& input,
437 const armnn::PermutationVector& mappings)
438{
439 // Add swizzle layer
440 armnn::IConnectableLayer* const layer = network.AddPermuteLayer(mappings);
441
442 BOOST_ASSERT(layer != nullptr);
443
444 // Connect input to swizzle layer
445 input.Connect(layer->GetInputSlot(0));
446
447 // Setup swizzled output
448 const armnn::TensorInfo outInfo = armnnUtils::Permuted(input.GetTensorInfo(), mappings);
449 layer->GetOutputSlot(0).SetTensorInfo(outInfo);
450
451 return *layer;
452}
453
454void SwizzleIn(armnn::INetwork& network, LayerInputHandle& input, armnn::IConnectableLayer& layer, unsigned int index)
455{
456 // Add swizzle layer
457 armnn::IConnectableLayer& swizzleLayer = AddPermuteLayer(network, input, NHWCToArmNN);
458 // Connect swizzled input to layer
459 swizzleLayer.GetOutputSlot(0).Connect(layer.GetInputSlot(index));
460}
461
462armnn::IConnectableLayer& DeswizzleOut(armnn::INetwork& network, armnn::IConnectableLayer& layer, unsigned int index)
463{
464 // Add deswizzle layer
465 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(network, layer.GetOutputSlot(index), ArmNNToNHWC);
466 return deswizzleLayer;
467}
468
469// only suitable for input/output slot index 0, for other slots, use SwizzleIn and DeswizzleOut directly
470armnn::IConnectableLayer& SwizzleInDeswizzleOut(armnn::INetwork& network,
471 LayerInputHandle& input,
472 armnn::IConnectableLayer& firstLayer,
473 armnn::IConnectableLayer& lastLayer)
474{
475 SwizzleIn(network, input, firstLayer, 0);
476 return DeswizzleOut(network, lastLayer, 0);
477}
478
479// only suitable for input/output slot index 0, for other slots, use SwizzleIn and DeswizzleOut directly
480armnn::IConnectableLayer& SwizzleInDeswizzleOut(armnn::INetwork& network, LayerInputHandle& input,
481 armnn::IConnectableLayer& layer)
482{
483 return SwizzleInDeswizzleOut(network, input, layer, layer);
484}
485
486bool ValidateConcatOutputShape(const std::vector<armnn::TensorShape> & inputShapes,
487 const armnn::TensorShape & outputShape,
488 uint32_t concatDim)
489{
490 // Validate the output shape is correct given the input shapes (which have just been validated)
491 unsigned int numDimensions = inputShapes[0].GetNumDimensions();
492 if (outputShape.GetNumDimensions() != numDimensions)
493 {
494 return Fail("%s: Output shape has wrong number of dimensions", __func__);
495 }
496
497 unsigned int outputSizeAlongConcatenatedDimension = 0;
498 for (unsigned int i = 0; i < inputShapes.size(); i++)
499 {
500 outputSizeAlongConcatenatedDimension += inputShapes[i][concatDim];
501 }
502
503 for (unsigned int i = 0; i < numDimensions; ++i)
504 {
505 if (i == concatDim)
506 {
507 if (outputShape[i] != outputSizeAlongConcatenatedDimension)
508 {
509 return Fail(
510 "%s: Invalid output shape for dimension %d (%d != %d)",
511 __func__,
512 i,
513 outputShape[i],
514 outputSizeAlongConcatenatedDimension);
515 }
516 }
517 else
518 {
519 if (outputShape[i] != inputShapes[0][i])
520 {
521 return Fail("%s: Invalid output shape", __func__);
522 }
523 }
524 }
525
526 return true;
527}
528
529bool RequiresReshape(armnn::TensorShape & inputShape)
530{
531 return inputShape.GetNumDimensions() < 3;
532}
533
arovir01b0717b52018-09-05 17:03:25 +0100534void SwizzleInputs(armnn::INetwork& network,
535 std::vector<LayerInputHandle>& inputs,
536 std::vector<armnn::TensorShape>& inputShapes,
537 const armnn::PermutationVector& mapping)
538{
539 if (!mapping.IsEqual(IdentityPermutation4D))
540 {
541 size_t nInputs = inputs.size();
542 for (size_t i=0; i<nInputs; ++i)
543 {
544 // add swizzle layer
545 armnn::IConnectableLayer& swizzleLayer = AddPermuteLayer(network, inputs[i], mapping);
546 auto& outputSlot = swizzleLayer.GetOutputSlot(0);
547 auto& outputInfo = outputSlot.GetTensorInfo();
548 // replace inputs with the swizzled ones
549 inputs[i] = LayerInputHandle(true, &outputSlot, outputInfo);
550 inputShapes[i] = inputs[i].GetTensorInfo().GetShape();
551 }
552 }
553}
554
narpra01f176d5a2018-11-18 20:17:48 +0000555bool CreateConcatPermutationParameters(const unsigned int numberOfDimensions,
556 int32_t & concatDimension,
557 std::pair<armnn::PermutationVector, armnn::PermutationVector> & permutationPair)
arovir01b0717b52018-09-05 17:03:25 +0100558{
narpra01f176d5a2018-11-18 20:17:48 +0000559 bool needPermute = false;
arovir01b0717b52018-09-05 17:03:25 +0100560 BOOST_ASSERT(numberOfDimensions >= 3);
561
562 // ArmNN uses Compute Library subtensors to perform concatenation
narpra01f176d5a2018-11-18 20:17:48 +0000563 // This only works when concatenating along dimension 0, 1 or 3 for a 4-D tensor,
564 // or along dimension 0 or 2 for a 3-D tensor.
565 if (numberOfDimensions == 4 && concatDimension == 2)
arovir01b0717b52018-09-05 17:03:25 +0100566 {
narpra01f176d5a2018-11-18 20:17:48 +0000567 concatDimension = 1;
568 permutationPair = std::make_pair(SwapDim1And2, SwapDim1And2);
569 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100570 }
narpra01f176d5a2018-11-18 20:17:48 +0000571 else if (numberOfDimensions == 3 && concatDimension == 1)
arovir01b0717b52018-09-05 17:03:25 +0100572 {
narpra01f176d5a2018-11-18 20:17:48 +0000573 concatDimension = 0;
574 permutationPair = std::make_pair(RotateTensorLeft, RotateTensorRight);
575 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100576 }
narpra01f176d5a2018-11-18 20:17:48 +0000577 return needPermute;
arovir01b0717b52018-09-05 17:03:25 +0100578}
579
580} // anonymous namespace
581
582namespace armnn_driver
583{
584
585//// Creates an ArmNN activation layer and connects it to the given layer, if the
586//// passed in AndroidNN activation function requires so.
587//// @return The end layer of the sequence of layers built for the given AndroidNN
588//// activation function or nullptr if an error occurred (e.g. unsupported activation).
589//// Note that the end layer matches the input layer if no activation is required
590//// (the sequence of layers has length 1).
591armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
592 ActivationFn activation,
593 armnn::IConnectableLayer* prevLayer,
594 ConversionData& data);
595
596} // namespace armnn_driver
597
598///
599/// Utility templates
600///
601
602namespace armnn_driver
603{
604
605using namespace android::nn;
606
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100607template<typename HalPolicy,
608 typename HalOperand = typename HalPolicy::Operand,
609 typename HalOperation = typename HalPolicy::Operation,
610 typename HalModel = typename HalPolicy::Model>
611const HalOperand* GetInputOperand(const HalOperation& operation,
612 uint32_t inputIndex,
613 const HalModel& model,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100614 bool failOnIndexOutOfBounds = true)
arovir01b0717b52018-09-05 17:03:25 +0100615{
616 if (inputIndex >= operation.inputs.size())
617 {
saoste01b8471482018-10-10 09:44:51 +0100618 if (failOnIndexOutOfBounds)
619 {
620 Fail("%s: invalid input index: %i out of %i", __func__, inputIndex, operation.inputs.size());
621 }
arovir01b0717b52018-09-05 17:03:25 +0100622 return nullptr;
623 }
624
625 BOOST_ASSERT(operation.inputs[inputIndex] < model.operands.size()); // Model should have been validated beforehand
626 return &model.operands[operation.inputs[inputIndex]];
627}
628
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100629template<typename HalPolicy,
630 typename HalOperand = typename HalPolicy::Operand,
631 typename HalOperation = typename HalPolicy::Operation,
632 typename HalModel = typename HalPolicy::Model>
633const HalOperand* GetOutputOperand(const HalOperation& operation,
634 uint32_t outputIndex,
635 const HalModel& model)
arovir01b0717b52018-09-05 17:03:25 +0100636{
637 if (outputIndex >= operation.outputs.size())
638 {
639 Fail("%s: invalid output index: %i out of %i", __func__, outputIndex, operation.outputs.size());
640 return nullptr;
641 }
642
643 // Model should have been validated beforehand
644 BOOST_ASSERT(operation.outputs[outputIndex] < model.operands.size());
645
646 return &model.operands[operation.outputs[outputIndex]];
647}
648
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100649template<typename HalPolicy,
Pablo Tellofb45e2f2019-10-18 16:51:57 +0100650 typename HalOperand = typename HalPolicy::Operand,
651 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100652const void* GetOperandValueReadOnlyAddress(const HalOperand& operand,
Matthew Bentham912b3622019-05-03 15:49:14 +0100653 const HalModel& model,
654 const ConversionData& data,
Kevin Mayf29a2c52019-03-14 11:56:32 +0000655 bool optional = false)
arovir01b0717b52018-09-05 17:03:25 +0100656{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100657 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
arovir01b0717b52018-09-05 17:03:25 +0100658
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100659 const void* valueStart = nullptr;
arovir01b0717b52018-09-05 17:03:25 +0100660 switch (operand.lifetime)
661 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100662 case HalOperandLifeTime::CONSTANT_COPY:
arovir01b0717b52018-09-05 17:03:25 +0100663 {
664 // Constant found in model.operandValues
665 valueStart = &model.operandValues[operand.location.offset];
666 break;
667 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100668 case HalOperandLifeTime::CONSTANT_REFERENCE:
arovir01b0717b52018-09-05 17:03:25 +0100669 {
670 // Constant specified via a Memory object
671 valueStart = GetMemoryFromPool(operand.location, data.m_MemPools);
672 break;
673 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100674 case HalOperandLifeTime::NO_VALUE:
Kevin Mayf29a2c52019-03-14 11:56:32 +0000675 {
676 // An optional input tensor with no values is not an error so should not register as a fail
677 if (optional)
678 {
679 valueStart = nullptr;
680 break;
681 }
Matthew Bentham912b3622019-05-03 15:49:14 +0100682 [[fallthrough]];
Kevin Mayf29a2c52019-03-14 11:56:32 +0000683 }
arovir01b0717b52018-09-05 17:03:25 +0100684 default:
685 {
686 // Unsupported/invalid (e.g. can't get value of an input to the model)
687 Fail("%s: unsupported/invalid operand lifetime: %s",
688 __func__, toString(operand.lifetime).c_str());
689 valueStart = nullptr;
690 }
691 }
692
693 return valueStart;
694}
695
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100696template<typename HalPolicy,
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100697 typename HalOperation = typename HalPolicy::Operation,
698 typename HalModel = typename HalPolicy::Model,
699 typename HalOperandType = typename HalPolicy::OperandType>
700bool GetOperandType(const HalOperation& operation,
701 uint32_t inputIndex,
702 const HalModel& model,
703 HalOperandType& type)
704{
705 using HalOperand = typename HalPolicy::Operand;
706
707 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
708 if (!operand)
709 {
710 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
711 }
712
713 type = operand->type;
714 return true;
715}
716
717template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100718 typename HalOperand = typename HalPolicy::Operand,
719 typename HalModel = typename HalPolicy::Model>
720ConstTensorPin ConvertOperandToConstTensorPin(const HalOperand& operand,
721 const HalModel& model,
722 const ConversionData& data,
723 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
724 const armnn::TensorShape* overrideTensorShape = nullptr,
725 bool optional = false)
726{
727 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
728
729 if (!IsOperandTypeSupportedForTensors(operand.type))
730 {
731 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand.type).c_str());
732 return ConstTensorPin();
733 }
734
735 if (!optional &&
736 operand.lifetime != HalOperandLifeTime::CONSTANT_COPY &&
737 operand.lifetime != HalOperandLifeTime::CONSTANT_REFERENCE &&
738 operand.lifetime != HalOperandLifeTime::NO_VALUE)
739 {
740 Fail("%s: invalid operand lifetime: %s", __func__, toString(operand.lifetime).c_str());
741 return ConstTensorPin();
742 }
743
744 const void* const valueStart = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data, optional);
745 if (!valueStart)
746 {
747 if (optional)
748 {
749 // optional tensor with no values is not really an error; return it as invalid, but marked as optional
750 return ConstTensorPin(true);
751 }
752 // mandatory tensor with no values
753 Fail("%s: failed to get operand address", __func__);
754 return ConstTensorPin();
755 }
756
757 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(operand);
Teresa Charlin02dce092019-11-11 17:06:23 +0000758 // Android datalayout might be different than armnn datalayout, e.g. the kernel for the depthwise convolution.
759 if (tensorInfo.HasPerAxisQuantization())
760 {
761 tensorInfo.SetQuantizationDim(dimensionMappings[tensorInfo.GetQuantizationDim().value()]);
762 }
763
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100764 if (overrideTensorShape != nullptr)
765 {
766 tensorInfo.SetShape(*overrideTensorShape);
767 }
768 return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings);
769}
770
771template<typename HalPolicy,
772 typename HalOperation = typename HalPolicy::Operation,
773 typename HalModel = typename HalPolicy::Model>
774ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operation,
775 uint32_t inputIndex,
776 const HalModel& model,
777 const ConversionData& data,
778 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
779 const armnn::TensorShape* overrideTensorShape = nullptr,
780 bool optional = false)
781{
782 using HalOperand = typename HalPolicy::Operand;
783
784 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
785 if (!operand)
786 {
787 Fail("%s: failed to get input operand: index=%u", __func__, inputIndex);
788 return ConstTensorPin();
789 }
790 return ConvertOperandToConstTensorPin<HalPolicy>(*operand,
791 model,
792 data,
793 dimensionMappings,
794 overrideTensorShape,
795 optional);
796}
797
798template<typename HalPolicy,
799 typename OutputType,
800 typename HalOperandType = typename HalPolicy::OperandType,
801 typename HalOperation = typename HalPolicy::Operation,
802 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100803bool GetInputScalar(const HalOperation& operation,
804 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100805 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100806 OutputType& outValue,
807 const HalModel& model,
808 const ConversionData& data)
809{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100810 using HalOperand = typename HalPolicy::Operand;
811
812 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +0100813 if (!operand)
814 {
815 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
816 }
817
818 if (operand->type != type)
819 {
820 return Fail("%s: unexpected operand type: %s (should be %s)",
821 __func__, toString(operand->type).c_str(), toString(type).c_str());
822 }
823
824 if (operand->location.length != sizeof(OutputType))
825 {
826 return Fail("%s: incorrect operand location length: %i (should be %i)",
827 __func__, operand->location.length, sizeof(OutputType));
828 }
829
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100830 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100831 if (!valueAddress)
832 {
833 return Fail("%s: failed to get address for operand", __func__);
834 }
835
836 outValue = *(static_cast<const OutputType*>(valueAddress));
837 return true;
838}
839
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100840template<typename HalPolicy,
841 typename HalOperation = typename HalPolicy::Operation,
842 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100843bool GetInputInt32(const HalOperation& operation,
844 uint32_t inputIndex,
845 int32_t& outValue,
846 const HalModel& model,
847 const ConversionData& data)
848{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100849 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::INT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100850}
851
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100852template<typename HalPolicy,
853 typename HalOperation = typename HalPolicy::Operation,
854 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100855bool GetInputFloat32(const HalOperation& operation,
856 uint32_t inputIndex,
857 float& outValue,
858 const HalModel& model,
859 const ConversionData& data)
860{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100861 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::FLOAT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100862}
863
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100864template<typename HalPolicy,
865 typename HalOperation = typename HalPolicy::Operation,
866 typename HalOperandType = typename HalPolicy::OperandType,
867 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100868bool GetInputActivationFunctionImpl(const HalOperation& operation,
869 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100870 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100871 ActivationFn& outActivationFunction,
872 const HalModel& model,
873 const ConversionData& data)
874{
Mike Kellyb5fdf382019-06-11 16:35:25 +0100875 if (type != HalOperandType::INT32 && type != HalOperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100876 {
877 return Fail("%s: unexpected operand type: %s (should be %s or %s)",
878 __func__,
879 toString(type).c_str(),
880 toString(OperandType::INT32).c_str(),
881 toString(OperandType::TENSOR_INT32).c_str());
882 }
883
884 int32_t activationFunctionAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100885 if (!GetInputScalar<HalPolicy>(operation, inputIndex, type, activationFunctionAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100886 {
887 return Fail("%s: failed to get activation input value", __func__);
888 }
889 outActivationFunction = static_cast<ActivationFn>(activationFunctionAsInt);
890 return true;
891}
892
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100893template<typename HalPolicy,
894 typename HalOperation = typename HalPolicy::Operation,
895 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100896bool GetInputActivationFunction(const HalOperation& operation,
897 uint32_t inputIndex,
898 ActivationFn& outActivationFunction,
899 const HalModel& model,
900 const ConversionData& data)
901{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100902 return GetInputActivationFunctionImpl<HalPolicy>(operation,
903 inputIndex,
904 HalPolicy::OperandType::INT32,
905 outActivationFunction,
906 model,
907 data);
arovir01b0717b52018-09-05 17:03:25 +0100908}
909
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100910template<typename HalPolicy,
911 typename HalOperation = typename HalPolicy::Operation,
912 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100913bool GetInputActivationFunctionFromTensor(const HalOperation& operation,
914 uint32_t inputIndex,
915 ActivationFn& outActivationFunction,
916 const HalModel& model,
917 const ConversionData& data)
918{
919 // This only accepts a 1-D tensor of size 1
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100920 return GetInputActivationFunctionImpl<HalPolicy>(operation,
921 inputIndex,
922 HalPolicy::OperandType::INT32,
923 outActivationFunction,
924 model,
925 data);
arovir01b0717b52018-09-05 17:03:25 +0100926}
927
928
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100929template<typename HalPolicy,
930 typename HalOperation = typename HalPolicy::Operation,
931 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100932bool GetOptionalInputActivation(const HalOperation& operation,
933 uint32_t inputIndex,
934 ActivationFn& activationFunction,
935 const HalModel& model,
936 const ConversionData& data)
937{
938 if (operation.inputs.size() <= inputIndex)
939 {
940 activationFunction = ActivationFn::kActivationNone;
941 }
942 else
943 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100944 if (!GetInputActivationFunction<HalPolicy>(operation, inputIndex, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100945 {
946 return Fail("%s: Operation has invalid inputs", __func__);
947 }
948 }
949 return true;
950}
951
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100952template<typename HalPolicy,
953 typename ConvolutionDescriptor,
954 typename HalOperation = typename HalPolicy::Operation,
955 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100956bool GetOptionalConvolutionDilationParams(const HalOperation& operation,
957 uint32_t dilationXIndex,
958 ConvolutionDescriptor& descriptor,
959 const HalModel& model,
960 const ConversionData& data)
961{
962 bool success = true;
963 if (operation.inputs.size() >= dilationXIndex + 2)
964 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100965 success &= GetInputScalar<HalPolicy>(operation,
966 dilationXIndex,
967 HalPolicy::OperandType::INT32,
968 descriptor.m_DilationX,
969 model,
970 data);
971 success &= GetInputScalar<HalPolicy>(operation,
972 dilationXIndex + 1,
973 HalPolicy::OperandType::INT32,
974 descriptor.m_DilationY,
975 model,
976 data);
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100977 }
978
979 return success;
980}
981
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100982template<typename HalPolicy,
983 typename HalOperand = typename HalPolicy::Operand,
984 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100985bool GetTensorInt32Values(const HalOperand& operand,
arovir01b0717b52018-09-05 17:03:25 +0100986 std::vector<int32_t>& outValues,
987 const HalModel& model,
988 const ConversionData& data)
989{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100990 if (operand.type != HalPolicy::OperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100991 {
992 return Fail("%s: invalid operand type: %s", __func__, toString(operand.type).c_str());
993 }
994
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100995 const void* startAddress = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100996 if (!startAddress)
997 {
998 return Fail("%s: failed to get operand address", __func__, operand.type);
999 }
1000
1001 // Check number of bytes is sensible
1002 const uint32_t numBytes = operand.location.length;
1003 if (numBytes % sizeof(int32_t) != 0)
1004 {
1005 return Fail("%s: invalid number of bytes: %i, expected to be a multiple of %i",
1006 __func__, numBytes, sizeof(int32_t));
1007 }
1008
1009 outValues.resize(numBytes / sizeof(int32_t));
1010 memcpy(outValues.data(), startAddress, numBytes);
1011 return true;
1012}
1013
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001014template<typename HalPolicy,
1015 typename HalOperation = typename HalPolicy::Operation,
1016 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001017bool GetInputPaddingScheme(const HalOperation& operation,
1018 uint32_t inputIndex,
1019 PaddingScheme& outPaddingScheme,
1020 const HalModel& model,
1021 const ConversionData& data)
1022{
1023 int32_t paddingSchemeAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001024 if (!GetInputInt32<HalPolicy>(operation, inputIndex, paddingSchemeAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001025 {
1026 return Fail("%s: failed to get padding scheme input value", __func__);
1027 }
1028
1029 outPaddingScheme = static_cast<android::nn::PaddingScheme>(paddingSchemeAsInt);
1030 return true;
1031}
1032
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001033template<typename HalPolicy,
1034 typename HalOperation = typename HalPolicy::Operation,
1035 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001036LayerInputHandle ConvertToLayerInputHandle(const HalOperation& operation,
1037 uint32_t inputIndex,
1038 const HalModel& model,
1039 ConversionData& data)
1040{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001041 using HalOperand = typename HalPolicy::Operand;
Sadik Armagan44bcc022019-06-18 17:21:36 +01001042 using HalOperandType = typename HalPolicy::OperandType;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001043 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
1044
1045 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +01001046 if (!operand)
1047 {
1048 Fail("%s: failed to get input operand %i", __func__, inputIndex);
1049 return LayerInputHandle();
1050 }
1051
1052 if (!IsOperandTypeSupportedForTensors(operand->type))
1053 {
1054 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand->type).c_str());
1055 return LayerInputHandle();
1056 }
1057
Sadik Armagan44bcc022019-06-18 17:21:36 +01001058 try
arovir01b0717b52018-09-05 17:03:25 +01001059 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001060 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +01001061 if (IsDynamicTensor(operandTensorInfo))
1062 {
1063 Fail("%s: dynamic input tensors are not supported", __func__);
1064 return LayerInputHandle();
1065 }
arovir01b0717b52018-09-05 17:03:25 +01001066
Sadik Armagan44bcc022019-06-18 17:21:36 +01001067 switch (operand->lifetime)
arovir01b0717b52018-09-05 17:03:25 +01001068 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001069 case HalOperandLifeTime::MODEL_INPUT:
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001070 {
1071 // NOTE: We must check whether we can support the input tensor on at least one
1072 // of the provided backends; otherwise we cannot convert the operation
1073 bool isInputSupported = false;
1074 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1075 IsInputSupported,
1076 data.m_Backends,
1077 isInputSupported,
1078 operandTensorInfo);
1079
1080 if (!isInputSupported)
1081 {
1082 Fail("%s: unsupported input tensor", __func__);
1083 return LayerInputHandle();
1084 }
1085
1086 BOOST_FALLTHROUGH; // intentional fallthrough
1087 }
1088 case HalOperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001089 case HalOperandLifeTime::MODEL_OUTPUT:
arovir01b0717b52018-09-05 17:03:25 +01001090 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001091 // The tensor is either an operand internal to the model, or a model input.
1092 // It can be associated with an ArmNN output slot for an existing layer.
1093
1094 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
1095 const uint32_t operandIndex = operation.inputs[inputIndex];
1096 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
Sadik Armagan44bcc022019-06-18 17:21:36 +01001097 }
Aron Virginas-Tar000117b2019-07-25 16:24:49 +01001098 case HalOperandLifeTime::CONSTANT_COPY: // intentional fallthrough
Sadik Armagan44bcc022019-06-18 17:21:36 +01001099 case HalOperandLifeTime::CONSTANT_REFERENCE:
1100 {
1101 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
1102 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin<HalPolicy>(*operand, model, data);
1103 if (tensorPin.IsValid())
arovir01b0717b52018-09-05 17:03:25 +01001104 {
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001105 bool isSupported = false;
1106 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1107 IsConstantSupported,
1108 data.m_Backends,
1109 isSupported,
1110 tensorPin.GetConstTensor().GetInfo());
Mike Kelly28e3d9f2019-08-07 14:55:04 +01001111 if (!isSupported)
Sadik Armagan44bcc022019-06-18 17:21:36 +01001112 {
1113 return LayerInputHandle();
1114 }
1115
1116 armnn::IConnectableLayer* constantLayer =
1117 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
1118 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
1119 outputSlot.SetTensorInfo(tensorPin.GetConstTensor().GetInfo());
1120
1121 return LayerInputHandle(true, &outputSlot, operandTensorInfo);
1122 }
1123 else
1124 {
1125 Fail("%s: invalid operand tensor", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001126 return LayerInputHandle();
1127 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001128 break;
arovir01b0717b52018-09-05 17:03:25 +01001129 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001130 default:
arovir01b0717b52018-09-05 17:03:25 +01001131 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001132 // Unsupported lifetime for an input tensor
1133 Fail("%s: unsupported lifetime for input tensor: %s",
1134 __func__, toString(operand->lifetime).c_str());
arovir01b0717b52018-09-05 17:03:25 +01001135 return LayerInputHandle();
1136 }
arovir01b0717b52018-09-05 17:03:25 +01001137 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001138 }
1139 catch (UnsupportedOperand<HalOperandType>& e)
1140 {
1141 Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str());
1142 return LayerInputHandle();
arovir01b0717b52018-09-05 17:03:25 +01001143 }
1144}
1145
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001146template<typename HalPolicy,
1147 typename HalOperation = typename HalPolicy::Operation,
1148 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001149bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1150 uint32_t operationOutputIndex,
1151 armnn::IConnectableLayer& layer,
1152 uint32_t layerOutputIndex,
1153 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001154 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001155{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001156 using HalOperand = typename HalPolicy::Operand;
1157
1158 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001159 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
1160 {
1161 return false;
1162 }
1163
1164 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
1165
1166 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
1167 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
1168
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001169 outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand));
Mike Kellyb5fdf382019-06-11 16:35:25 +01001170
1171 return true;
1172}
1173
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001174template<typename HalPolicy,
1175 typename HalOperation = typename HalPolicy::Operation,
1176 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001177armnn::DataLayout OptionalDataLayout(const HalOperation& operation,
1178 uint32_t inputIndex,
1179 const HalModel& model,
1180 ConversionData& data)
1181{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001182 using HalOperand = typename HalPolicy::Operand;
1183
1184 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001185 if (!operand)
1186 {
1187 return armnn::DataLayout::NHWC;
1188 }
1189
1190 if (!IsBool(*operand))
1191 {
1192 return armnn::DataLayout::NHWC;
1193 }
1194
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001195 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001196 if (!valueAddress)
1197 {
1198 return armnn::DataLayout::NHWC;
1199 }
1200
1201 if (*(static_cast<const bool*>(valueAddress)))
1202 {
1203 return armnn::DataLayout::NCHW;
1204 }
1205 else
1206 {
1207 return armnn::DataLayout::NHWC;
1208 }
1209}
1210
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001211template<typename HalPolicy,
1212 typename HalOperation = typename HalPolicy::Operation,
1213 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001214bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1215 uint32_t outputIndex,
1216 armnn::IConnectableLayer& layer,
1217 const HalModel& model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001218 ConversionData& data)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001219{
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001220 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1221 outputIndex,
1222 layer,
1223 outputIndex,
1224 model,
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001225 data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001226}
1227
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001228template<typename HalPolicy,
1229 typename HalOperation = typename HalPolicy::Operation,
1230 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001231bool ConvertToActivation(const HalOperation& operation,
1232 const char* operationName,
1233 const armnn::ActivationDescriptor& activationDesc,
1234 const HalModel& model,
1235 ConversionData& data)
1236{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001237 using HalOperand = typename HalPolicy::Operand;
1238
1239 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001240 if (!input.IsValid())
1241 {
1242 return Fail("%s: Input 0 is invalid", operationName);
1243 }
1244
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001245 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001246 if (!outputOperand)
1247 {
1248 return false;
1249 }
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001250
1251 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Sadik Armagan2050c232019-07-23 16:59:58 +01001252 if (IsDynamicTensor(outInfo))
1253 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001254 return Fail("%s: Dynamic output tensors are not supported", __func__);
Sadik Armagan2050c232019-07-23 16:59:58 +01001255 }
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001256
1257 bool isSupported = false;
1258 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1259 IsActivationSupported,
1260 data.m_Backends,
1261 isSupported,
1262 input.GetTensorInfo(),
1263 outInfo,
1264 activationDesc);
1265 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001266 {
1267 return false;
1268 }
1269
1270 armnn::IConnectableLayer* layer = data.m_Network->AddActivationLayer(activationDesc);
1271 BOOST_ASSERT(layer != nullptr);
1272 input.Connect(layer->GetInputSlot(0));
1273
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001274 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001275}
1276
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001277template<typename HalPolicy,
Sadik Armagan61113162019-07-25 09:09:40 +01001278 typename HalOperation = typename HalPolicy::Operation,
1279 typename HalModel = typename HalPolicy::Model>
1280bool ConvertReLu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1281{
1282 armnn::ActivationDescriptor desc;
1283 desc.m_Function = armnn::ActivationFunction::ReLu;
1284
1285 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1286}
1287
1288template<typename HalPolicy,
1289 typename HalOperation = typename HalPolicy::Operation,
1290 typename HalModel = typename HalPolicy::Model>
1291bool ConvertReLu1(const HalOperation& operation, const HalModel& model, ConversionData& data)
1292{
1293 armnn::ActivationDescriptor desc;
1294 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1295 desc.m_A = 1.0f;
1296 desc.m_B = -1.0f;
1297
1298 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1299}
1300
1301template<typename HalPolicy,
1302 typename HalOperation = typename HalPolicy::Operation,
1303 typename HalModel = typename HalPolicy::Model>
1304bool ConvertReLu6(const HalOperation& operation, const HalModel& model, ConversionData& data)
1305{
1306 armnn::ActivationDescriptor desc;
1307 desc.m_Function = armnn::ActivationFunction::BoundedReLu;
1308 desc.m_A = 6.0f;
1309
1310 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1311}
1312
1313template<typename HalPolicy,
1314 typename HalOperation = typename HalPolicy::Operation,
1315 typename HalModel = typename HalPolicy::Model>
1316bool ConvertTanH(const HalOperation& operation, const HalModel& model, ConversionData& data)
1317{
1318 armnn::ActivationDescriptor desc;
1319 desc.m_Function = armnn::ActivationFunction::TanH;
1320 desc.m_A = 1.0f; // android nn does not support tanH parameters
1321 desc.m_B = 1.0f; // set to 1.0f for unity scaling
1322
1323 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
1324}
1325
1326template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001327 typename HalOperation = typename HalPolicy::Operation,
1328 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001329bool ConvertPaddings(const HalOperation& operation,
1330 const HalModel& model,
1331 ConversionData& data,
1332 unsigned int rank,
1333 armnn::PadDescriptor& padDescriptor)
1334{
1335 using HalOperand = typename HalPolicy::Operand;
1336
1337 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
1338 if (!paddingsOperand)
1339 {
1340 return Fail("%s: Could not read paddings operand", __func__);
1341 }
1342
1343 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
1344 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2)
1345 {
1346 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
1347 }
1348
1349 std::vector<int32_t> paddings;
1350 GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data);
1351
1352 // add padding for each dimension of input tensor.
1353 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
1354 {
1355 int paddingBeforeInput = paddings[i];
1356 int paddingAfterInput = paddings[i + 1];
1357
1358 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
1359 {
1360 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
1361 }
1362
1363 padDescriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
1364 }
1365
1366 return true;
1367}
1368
1369template<typename HalPolicy,
1370 typename HalOperation = typename HalPolicy::Operation,
1371 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001372bool ConvertPooling2d(const HalOperation& operation,
1373 const char* operationName,
1374 armnn::PoolingAlgorithm poolType,
1375 const HalModel& model,
1376 ConversionData& data)
1377{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001378 using HalOperand = typename HalPolicy::Operand;
1379 using HalOperandType = typename HalPolicy::OperandType;
1380
1381 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001382 if (!input.IsValid())
1383 {
1384 return Fail("%s: Could not read input 0", operationName);
1385 }
1386
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001387 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001388 if (!output)
1389 {
1390 return Fail("%s: Could not read output 0", __func__);
1391 }
1392
1393 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1394 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1395
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001396 if (IsDynamicTensor(outputInfo))
1397 {
1398 return Fail("%s: Dynamic output tensors are not supported", __func__);
1399 }
1400
arovir01b0717b52018-09-05 17:03:25 +01001401 armnn::Pooling2dDescriptor desc;
1402 desc.m_PoolType = poolType;
1403 desc.m_OutputShapeRounding = armnn::OutputShapeRounding::Floor;
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001404 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001405
1406 ActivationFn activation;
1407
Sadik Armagan15d63e22019-07-26 16:59:35 +01001408 auto inputSize = operation.inputs.size();
1409
1410 if (inputSize >= 10)
1411 {
1412 // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type)
1413 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1414 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1415 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1416 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1417 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1418 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1419 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1420 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1421 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
1422 {
1423 return Fail("%s: Operation has invalid inputs", operationName);
1424 }
1425
1426 if (Is12Operand(*output))
1427 {
1428 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
1429 }
1430 }
1431 else
arovir01b0717b52018-09-05 17:03:25 +01001432 {
1433 // one input, 6 parameters (padding, stridex, stridey, width, height, activation type)
1434 android::nn::PaddingScheme scheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001435 if (!GetInputPaddingScheme<HalPolicy>(operation, 1, scheme, model, data) ||
1436 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1437 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1438 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1439 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1440 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001441 {
1442 return Fail("%s: Operation has invalid inputs", operationName);
1443 }
1444
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001445 const unsigned int inputWidth = inputInfo.GetShape()[2];
1446 const unsigned int inputHeight = inputInfo.GetShape()[1];
arovir01b0717b52018-09-05 17:03:25 +01001447
1448 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, scheme);
1449 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, scheme);
Sadik Armagan15d63e22019-07-26 16:59:35 +01001450
1451 if (Is12Operand(*output))
arovir01b0717b52018-09-05 17:03:25 +01001452 {
Sadik Armagan15d63e22019-07-26 16:59:35 +01001453 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001454 }
1455 }
1456
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001457 bool isSupported = false;
1458 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1459 IsPooling2dSupported,
1460 data.m_Backends,
1461 isSupported,
1462 inputInfo,
1463 outputInfo,
1464 desc);
1465 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001466 {
Éanna Ó Catháin3d1059c2018-10-11 15:53:04 +01001467 return false;
arovir01b0717b52018-09-05 17:03:25 +01001468 }
arovir01b0717b52018-09-05 17:03:25 +01001469
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001470 armnn::IConnectableLayer* pooling2dLayer = data.m_Network->AddPooling2dLayer(desc);
1471 if (!pooling2dLayer)
arovir01b0717b52018-09-05 17:03:25 +01001472 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001473 return Fail("%s: AddPooling2dLayer failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001474 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001475
1476 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, pooling2dLayer, data);
1477 if (!endLayer)
arovir01b0717b52018-09-05 17:03:25 +01001478 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001479 return Fail("%s: ProcessActivation failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001480 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001481
1482 input.Connect(pooling2dLayer->GetInputSlot(0));
1483
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001484 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001485}
1486
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001487template<typename HalPolicy,
Mike Kellyb8805202019-07-31 17:25:43 +01001488 typename Operation = typename HalPolicy::Operation,
1489 typename Model = typename HalPolicy::Model>
Mike Kelly46272802019-08-14 17:00:48 +01001490bool ConvertAdd(const Operation& operation, const Model& model, ConversionData& data)
1491{
1492 using Operand = typename HalPolicy::Operand;
1493
1494 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1495 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1496
1497 if (!input0.IsValid() || !input1.IsValid())
1498 {
1499 return Fail("%s: Operation has invalid inputs", __func__);
1500 }
1501
1502 // The FuseActivation parameter is always the input index 2
1503 // and it should be optional
1504 ActivationFn activationFunction;
1505 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
1506 {
1507 return Fail("%s: Operation has invalid inputs", __func__);
1508 }
1509
1510 const Operand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1511 if (!outputOperand)
1512 {
1513 return false;
1514 }
1515
1516 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
1517 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
1518
1519 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
1520 if (IsDynamicTensor(outputInfo))
1521 {
1522 return Fail("%s: Dynamic output tensors are not supported", __func__);
1523 }
1524
1525 bool isSupported = false;
1526 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1527 IsAdditionSupported,
1528 data.m_Backends,
1529 isSupported,
1530 inputInfo0,
1531 inputInfo1,
1532 outputInfo);
1533 if (!isSupported)
1534 {
1535 return false;
1536 }
1537
1538 armnn::IConnectableLayer* const startLayer = data.m_Network->AddAdditionLayer();
1539 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
1540
1541 if (endLayer != nullptr)
1542 {
Sadik Armagan64b19b52019-08-19 09:49:58 +01001543 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
1544 if (!isReshapeSupported)
1545 {
1546 return false;
1547 }
1548
Mike Kelly46272802019-08-14 17:00:48 +01001549 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
1550 }
1551 else
1552 {
1553 return Fail("%s: ProcessActivation failed", __func__);
1554 }
1555}
1556
1557template<typename HalPolicy,
1558 typename Operation = typename HalPolicy::Operation,
1559 typename Model = typename HalPolicy::Model>
Mike Kellyb8805202019-07-31 17:25:43 +01001560bool ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data)
1561{
1562 using HalOperand = typename HalPolicy::Operand;
1563 using HalOperandType = typename HalPolicy::OperandType;
1564
1565 // The first N (0..N-1) inputs are tensors. The Nth input is the concatenation axis.
1566 if (operation.inputs.size() <= 1)
1567 {
1568 return Fail("%s: Operation has insufficient arguments", __func__);
1569 }
1570
1571 // Get inputs and outputs
1572 const std::size_t numInputTensors = operation.inputs.size() - 1;
1573
1574 int32_t concatDim;
1575 if (!GetInputScalar<HalPolicy>(operation, numInputTensors, HalOperandType::INT32, concatDim, model, data))
1576 {
1577 return Fail("%s: Operation has invalid inputs", __func__);
1578 }
1579
1580 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1581 if (!outputOperand)
1582 {
1583 return Fail("%s: Operation has no outputs", __func__);
1584 }
1585
1586
1587 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*outputOperand);
1588 armnn::TensorShape outputShape = outputInfo.GetShape();
1589
1590 //
1591 // handle negative concat dims along the lines of tensorflow as described here:
1592 // https://www.tensorflow.org/api_docs/python/tf/concat
1593 // "negative axis refers to axis + rank(values)-th dimension"
1594 //
1595 if (concatDim < 0)
1596 {
1597 concatDim += outputShape.GetNumDimensions();
1598 }
1599
1600 if (concatDim >= static_cast<int32_t>(outputShape.GetNumDimensions()) || concatDim < 0)
1601 {
1602 return Fail("%s: Operation has invalid concat axis: %d", __func__, concatDim);
1603 }
1604
1605 std::vector<LayerInputHandle> inputHandles;
1606 std::vector<armnn::TensorShape> inputShapes;
1607
1608 inputHandles.reserve(numInputTensors);
1609 inputShapes.reserve(numInputTensors);
1610
1611 bool inputsHaveBeenReshaped = false;
1612 unsigned int tensorDimensionsAdded = 0;
1613
1614 for (uint32_t i = 0; i < numInputTensors; ++i)
1615 {
1616 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, i, model);
1617 if (!operand)
1618 {
1619 return Fail("%s: Operation has invalid inputs", __func__);
1620 }
1621
Teresa Charlin3b959602019-10-31 17:05:47 +00001622 LayerInputHandle operandInputHandle = ConvertToLayerInputHandle<HalPolicy>(operation, i, model, data);
1623 if (!operandInputHandle.IsValid())
1624 {
1625 return Fail("%s: Operation has invalid inputs", __func__);
1626 }
Mike Kellyb8805202019-07-31 17:25:43 +01001627
Teresa Charlin3b959602019-10-31 17:05:47 +00001628 armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand);
Mike Kellyb8805202019-07-31 17:25:43 +01001629 if (operandShape.GetNumDimensions() == 0)
1630 {
1631 return Fail("%s: Operands with rank 0 are not supported", __func__);
1632 }
1633
1634 if (RequiresReshape(operandShape))
1635 {
1636 inputsHaveBeenReshaped = true;
1637
1638 armnn::TensorInfo reshapeInfo = operandInputHandle.GetTensorInfo();
1639
1640 // Expand the tensor to three dimensions
1641 if (operandShape.GetNumDimensions() == 2)
1642 {
1643 reshapeInfo.SetShape(armnn::TensorShape({1, operandShape[0], operandShape[1]}));
1644 tensorDimensionsAdded = 1;
1645 }
1646 else
1647 {
1648 reshapeInfo.SetShape(armnn::TensorShape({1, 1, operandShape[0]}));
1649 tensorDimensionsAdded = 2;
1650 }
1651
1652 armnn::IConnectableLayer& newReshape = AddReshapeLayer(
1653 *data.m_Network,
1654 operandInputHandle,
1655 reshapeInfo
1656 );
1657
1658 // Point to the reshape operation rather then the input operation
1659 operandShape = reshapeInfo.GetShape();
1660 operandInputHandle = LayerInputHandle(true, &newReshape.GetOutputSlot(0), reshapeInfo);
1661 }
1662
1663 inputShapes.emplace_back(operandShape);
1664 inputHandles.emplace_back(operandInputHandle);
1665
1666 if (!inputHandles.back().IsValid())
1667 {
1668 return Fail("%s: Operation has invalid inputs", __func__);
1669 }
1670 }
1671
1672 BOOST_ASSERT(inputShapes.size() == inputHandles.size());
1673
1674 if (inputsHaveBeenReshaped)
1675 {
1676 // Adjust the concatenation dimension by the amount of dimensions added (if any)
1677 concatDim += tensorDimensionsAdded;
1678
1679 // Add extra dimensions to the output shape to reflect the addition of the reshape layers
1680 if (tensorDimensionsAdded == 1)
1681 {
1682 outputShape = armnn::TensorShape({1, outputShape[0], outputShape[1]});
1683 }
1684 else if (tensorDimensionsAdded == 2)
1685 {
1686 outputShape = armnn::TensorShape({1, 1, outputShape[0]});
1687 }
1688 }
1689
1690 // Check if permutations is required and get the pair of permutations required for the concatenation.
1691 // Permutation is required when the concat dimension is 2 for a 4D tensor or 1 for a 3D tensor.
1692 std::pair<armnn::PermutationVector, armnn::PermutationVector> permutationPair =
1693 std::make_pair(IdentityPermutation4D, IdentityPermutation4D);
1694
1695 bool needPermute =
1696 CreateConcatPermutationParameters(inputShapes[0].GetNumDimensions(), concatDim, permutationPair);
1697
1698 if (needPermute)
1699 {
1700 outputShape = armnnUtils::Permuted(outputShape, permutationPair.first);
1701 }
1702
1703 outputInfo.SetShape(outputShape);
1704
1705 // this is no-op for identity swizzles, otherwise it replaces both
1706 // the handles and shapes with the swizzled layer output handles and shapes
1707 SwizzleInputs(*data.m_Network, inputHandles, inputShapes, permutationPair.first);
1708
1709 // Create an armnn concat layer descriptor - this will also perform validation on the input shapes
1710 armnn::OriginsDescriptor concatDescriptor;
1711
1712 try
1713 {
1714 // The concat descriptor is always created across the only supported concat dimension
1715 // which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
1716 concatDescriptor =
1717 armnn::CreateDescriptorForConcatenation(inputShapes.begin(), inputShapes.end(), concatDim);
1718 }
1719 catch (const armnn::Exception& error)
1720 {
1721 return Fail("%s: Error preparing concat descriptor. %s", __func__, error.what());
1722 }
1723
1724 // Validate the output shape is correct given the input shapes based on the
1725 // only valid concat dimension which is 0, 1 or 3 for a 4-D tensor, or 0 or 2 for a 3-D tensor.
1726 if (!ValidateConcatOutputShape(inputShapes, outputShape, concatDim))
1727 {
1728 return Fail("%s: Error validating the output shape for concat", __func__);
1729 }
1730
1731 std::vector<const armnn::TensorInfo*> inputTensorInfos;
1732 std::transform(inputHandles.begin(), inputHandles.end(), std::back_inserter(inputTensorInfos),
1733 [](const LayerInputHandle& h) -> const armnn::TensorInfo*{ return &h.GetTensorInfo(); });
1734
1735 bool isSupported = false;
1736 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1737 IsConcatSupported,
1738 data.m_Backends,
1739 isSupported,
1740 inputTensorInfos,
1741 outputInfo,
1742 concatDescriptor);
1743 if (!isSupported)
1744 {
1745 return false;
1746 }
1747
1748 armnn::IConnectableLayer* layer = data.m_Network->AddConcatLayer(concatDescriptor);
1749 assert(layer != nullptr);
1750 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1751
1752 // Connect inputs to the layer
1753 const int numInputSlots = layer->GetNumInputSlots();
1754 assert(static_cast<std::size_t>(numInputSlots) == inputHandles.size());
1755 for (int i = 0; i < numInputSlots; ++i)
1756 {
1757 // connect the input directly to the merge (concat) layer
1758 inputHandles[static_cast<unsigned int>(i)].Connect(layer->GetInputSlot(i));
1759 }
1760
1761 if (needPermute)
1762 {
1763 // Add permutation layer and connect the output to it, the permutation becomes the output layer
1764 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(*data.m_Network,
1765 layer->GetOutputSlot(0),
1766 permutationPair.second);
1767 layer = &deswizzleLayer;
1768 }
1769
1770 if (inputsHaveBeenReshaped)
1771 {
1772 armnn::TensorInfo afterConcatInfo = layer->GetOutputSlot(0).GetTensorInfo();
1773
1774 // Undo the reshape knowing the amount of dimensions added
1775 if (tensorDimensionsAdded == 1)
1776 {
1777 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[1],
1778 afterConcatInfo.GetShape()[2] }));
1779 }
1780 else if (tensorDimensionsAdded == 2)
1781 {
1782 afterConcatInfo.SetShape(armnn::TensorShape({ afterConcatInfo.GetShape()[2] }));
1783 }
1784
1785 layer = &AddReshapeLayer(
1786 *data.m_Network,
1787 layer->GetOutputSlot(0),
1788 afterConcatInfo
1789 );
1790 }
1791
1792 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1793}
1794
1795template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001796 typename HalOperation = typename HalPolicy::Operation,
1797 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001798bool ConvertConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1799{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001800 using HalOperand = typename HalPolicy::Operand;
1801 using HalOperandType = typename HalPolicy::OperandType;
1802
1803 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001804 if (!input.IsValid())
1805 {
1806 return Fail("%s: Operation has invalid inputs", __func__);
1807 }
1808
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001809 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001810 if (!output)
1811 {
1812 return Fail("%s: Could not read output 0", __func__);
1813 }
1814
1815 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001816 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001817
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001818 if (IsDynamicTensor(outputInfo))
1819 {
1820 return Fail("%s: Dynamic output tensors are not supported", __func__);
1821 }
1822
Mike Kellyb5fdf382019-06-11 16:35:25 +01001823 // ArmNN does not currently support non-fixed weights or bias
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001824 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1825 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001826
1827 if (!weightsPin.IsValid() || !biasPin.IsValid())
1828 {
1829 return Fail("%s: Operation has invalid inputs", __func__);
1830 }
1831
1832 armnn::ConstTensor weights = weightsPin.GetConstTensor();
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001833 armnn::ConstTensor bias = biasPin.GetConstTensor();
Mike Kellyb5fdf382019-06-11 16:35:25 +01001834 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1835
1836 armnn::Convolution2dDescriptor desc;
1837 desc.m_DataLayout = armnn::DataLayout::NHWC;
1838 ActivationFn activation;
1839
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001840 if (operation.inputs.size() == 10)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001841 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001842 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1843 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1844 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1845 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1846 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1847 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001848 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001849 {
1850 return Fail("%s: Operation has invalid inputs", __func__);
1851 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01001852 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001853 else if (operation.inputs.size() == 7)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001854 {
1855 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001856 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1857 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1858 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01001859 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001860 {
1861 return Fail("%s: Operation has invalid inputs", __func__);
1862 }
1863
1864 const uint32_t kernelX = weights.GetShape()[2];
1865 const uint32_t kernelY = weights.GetShape()[1];
1866 const uint32_t inputX = inputInfo.GetShape()[2];
1867 const uint32_t inputY = inputInfo.GetShape()[1];
1868
1869 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1870 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001871 }
1872 else
1873 {
1874 return Fail("%s: Unsupported number of operation inputs", __func__);
1875 }
1876
1877 desc.m_BiasEnabled = true;
1878 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1879
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001880 bool isSupported = false;
1881 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1882 IsConvolution2dSupported,
1883 data.m_Backends,
1884 isSupported,
1885 inputInfo,
1886 outputInfo,
1887 desc,
1888 weights.GetInfo(),
1889 biases);
1890 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001891 {
1892 return false;
1893 }
1894
1895 armnn::IConnectableLayer* startLayer =
1896 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1897
1898 if (!startLayer)
1899 {
1900 return Fail("%s: AddConvolution2dLayer failed", __func__);
1901 }
1902
1903 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1904
1905 if (!endLayer)
1906 {
1907 return Fail("%s: ProcessActivation failed", __func__);
1908 }
1909
1910 input.Connect(startLayer->GetInputSlot(0));
1911
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01001912 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001913}
1914
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001915template<typename HalPolicy,
1916 typename HalOperation = typename HalPolicy::Operation,
1917 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar8edb16d2019-10-01 13:34:59 +01001918bool ConvertDepthToSpace(const HalOperation& operation, const HalModel& model, ConversionData& data)
1919{
1920 using HalOperand = typename HalPolicy::Operand;
1921 using HalOperandType = typename HalPolicy::OperandType;
1922
1923 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1924 if (!input.IsValid() )
1925 {
1926 return Fail("%s: Operation has invalid inputs", __func__);
1927 }
1928
1929 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1930 unsigned int rank = inputInfo.GetNumDimensions();
1931 if (rank != 4)
1932 {
1933 return Fail("%s: Only inputs with rank 4 are supported", __func__);
1934 }
1935
1936 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1937 if (!output)
1938 {
1939 return Fail("%s: Could not read output 0", __func__);
1940 }
1941
1942 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1943 if (IsDynamicTensor(outputInfo))
1944 {
1945 return Fail("%s: Dynamic output tensors are not supported", __func__);
1946 }
1947
1948 armnn::DepthToSpaceDescriptor descriptor;
1949
1950 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, descriptor.m_BlockSize, model, data);
1951 if (descriptor.m_BlockSize <= 1)
1952 {
1953 return Fail("%s: Block size must be at least 1 in all dimensions");
1954 }
1955
1956 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
1957 if (Is12Operand(*output))
1958 {
1959 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
1960 }
1961
1962 bool isSupported = false;
1963 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1964 IsDepthToSpaceSupported,
1965 data.m_Backends,
1966 isSupported,
1967 inputInfo,
1968 outputInfo,
1969 descriptor);
1970 if (!isSupported)
1971 {
1972 return false;
1973 }
1974
1975 armnn::IConnectableLayer* const layer = data.m_Network->AddDepthToSpaceLayer(descriptor);
1976 assert(layer != nullptr);
1977 input.Connect(layer->GetInputSlot(0));
1978
1979 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
1980}
1981
1982template<typename HalPolicy,
1983 typename HalOperation = typename HalPolicy::Operation,
1984 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001985bool ConvertDepthwiseConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1986{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001987 using HalOperand = typename HalPolicy::Operand;
1988 using HalOperandType = typename HalPolicy::OperandType;
1989
1990 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001991
1992 if (!input.IsValid())
1993 {
1994 return Fail("%s: Operation has invalid inputs", __func__);
1995 }
1996
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001997 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001998
1999 if (!output)
2000 {
2001 return Fail("%s: Could not read output 0", __func__);
2002 }
2003
2004 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002005 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002006
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002007 if (IsDynamicTensor(outputInfo))
2008 {
2009 return Fail("%s: Dynamic output tensors are not supported", __func__);
2010 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01002011
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002012 // ArmNN does not currently support non-fixed weights or bias
Mike Kellyb5fdf382019-06-11 16:35:25 +01002013 // 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 +01002014 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002015
2016 if (weightsOperand == nullptr)
2017 {
2018 return Fail("%s: Operand is invalid", __func__);
2019 }
2020 armnn::DepthwiseConvolution2dDescriptor desc;
2021 desc.m_DataLayout = armnn::DataLayout::NHWC;
2022
Mike Kellyb5fdf382019-06-11 16:35:25 +01002023 // Reinterpret weight data as [ H, W, I, M ]
2024 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
2025 weightsOperand->dimensions[2],
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002026 inputInfo.GetShape()[3],
2027 weightsOperand->dimensions[3] / inputInfo.GetShape()[3] });
Mike Kellyb5fdf382019-06-11 16:35:25 +01002028
2029 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
2030 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
2031
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002032 const ConstTensorPin weightsPin =
2033 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2034 1,
2035 model,
2036 data,
2037 HWIMToMIHW,
2038 &weightsShape);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002039
2040 // Bias is a 1D tensor
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002041 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01002042
2043 if (!weightsPin.IsValid() || !biasPin.IsValid())
2044 {
2045 return Fail("%s: Operation has invalid inputs", __func__);
2046 }
2047
2048 armnn::ConstTensor weights = weightsPin.GetConstTensor();
2049 armnn::ConstTensor bias = biasPin.GetConstTensor();
2050 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2051
2052 ActivationFn activation;
2053
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002054 if (operation.inputs.size() == 11)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002055 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002056 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
2057 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
2058 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
2059 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
2060 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2061 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002062 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002063 {
2064 return Fail("%s: Operation has invalid inputs", __func__);
2065 }
2066 }
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002067 else if (operation.inputs.size() == 8)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002068 {
2069 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01002070 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
2071 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
2072 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002073 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01002074 {
2075 return Fail("%s: Operation has invalid inputs", __func__);
2076 }
2077
2078 const uint32_t kernelX = weights.GetShape()[3];
2079 const uint32_t kernelY = weights.GetShape()[2];
Aron Virginas-Tara5e2a452019-07-29 16:13:19 +01002080 const uint32_t inputX = inputInfo.GetShape()[2];
2081 const uint32_t inputY = inputInfo.GetShape()[1];
Mike Kellyb5fdf382019-06-11 16:35:25 +01002082
2083 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
2084 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
2085 }
2086 else
2087 {
2088 return Fail("%s: Unsupported number of operation inputs", __func__);
2089 }
2090
2091 desc.m_BiasEnabled = true;
2092 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
2093
Ferran Balaguerd30093c2019-07-09 17:04:47 +01002094 bool isSupported = false;
2095 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2096 IsDepthwiseConvolutionSupported,
2097 data.m_Backends,
2098 isSupported,
2099 inputInfo,
2100 outputInfo,
2101 desc,
2102 weights.GetInfo(),
2103 biases);
2104 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01002105 {
2106 return false;
2107 }
2108
2109 armnn::IConnectableLayer* startLayer =
2110 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2111 if (!startLayer)
2112 {
2113 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
2114 }
2115
2116 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
2117 if (!endLayer)
2118 {
2119 return Fail("%s: ProcessActivation failed", __func__);
2120 }
2121
2122 input.Connect(startLayer->GetInputSlot(0));
2123
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002124 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
arovir01b0717b52018-09-05 17:03:25 +01002125}
2126
Mike Kelly3c673942019-07-25 09:26:06 +01002127template<typename HalPolicy,
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002128 typename HalOperation = typename HalPolicy::Operation,
2129 typename HalModel = typename HalPolicy::Model>
2130bool IsOperandConstant(const HalOperation& operation,
2131 uint32_t inputIndex,
2132 const HalModel& model,
2133 bool& isConstant)
2134{
2135 using HalOperand = typename HalPolicy::Operand;
2136 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
2137
2138 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
2139 if (!operand)
2140 {
2141 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
2142 }
2143
2144 isConstant = operand->lifetime == HalOperandLifeTime::CONSTANT_COPY ||
2145 operand->lifetime == HalOperandLifeTime::CONSTANT_REFERENCE ||
2146 operand->lifetime == HalOperandLifeTime::NO_VALUE;
2147
2148 return true;
2149}
2150
2151template<typename HalPolicy,
Mike Kelly46272802019-08-14 17:00:48 +01002152 typename Operation = typename HalPolicy::Operation,
2153 typename Model = typename HalPolicy::Model>
2154bool ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data)
Mike Kelly3c673942019-07-25 09:26:06 +01002155{
Mike Kelly46272802019-08-14 17:00:48 +01002156 using Operand = typename HalPolicy::Operand;
2157
2158 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2159 if (!input.IsValid())
2160 {
2161 return Fail("%s: Operation has invalid input", __func__);
2162 }
2163
2164 const Operand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2165 if (!outputOperand)
2166 {
2167 return Fail("%s: Operation has invalid outputs", __func__);
2168 }
2169
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002170 // If the output is going into the FC weights and input is const just return true
2171 const size_t outputIndex = operation.outputs[0];
2172 bool input_is_constant = false;
2173 if (!IsOperandConstant<HalPolicy>(operation,0,model,input_is_constant) && input_is_constant)
2174 {
2175 return Fail("Non const input not supported");
2176 }
2177
2178 // Iterate through the nodes and find the operation feeding from the Dequantize output operand
2179 for (uint32_t operationIdx = 0; operationIdx < model.operations.size(); ++operationIdx)
2180 {
2181 // Search for the FC op which consumes the output of Dequantize with index equal to outputIndex
2182 const auto& operationIt = model.operations[operationIdx];
2183 switch (operationIt.type)
2184 {
2185 case HalPolicy::OperationType::FULLY_CONNECTED:
2186 if (outputIndex == operationIt.inputs[1]) // Weights are bound to slot 1
2187 {
2188 // If the output is going into the FC weights and input is const just return true
2189 return true;
2190 }
2191 break;
2192 case HalPolicy::OperationType::LSTM:
2193 for (size_t k = 0; k < operationIt.inputs.size(); ++k)
2194 {
2195 if (outputIndex == operationIt.inputs[k])
2196 {
2197 // If the output is going into the LSTM weights and input is const just return true
2198 return true;
2199 }
2200 }
2201 break;
2202 default:
2203 break;
2204 }
2205 }
2206
Mike Kelly46272802019-08-14 17:00:48 +01002207 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2208 if (IsDynamicTensor(outputInfo))
2209 {
2210 return Fail("%s: Dynamic output tensors are not supported", __func__);
2211 }
2212
2213 bool isSupported = false;
2214 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2215 IsDequantizeSupported,
2216 data.m_Backends,
2217 isSupported,
2218 input.GetTensorInfo(),
2219 GetTensorInfoForOperand(*outputOperand));
2220 if (!isSupported)
2221 {
2222 return false;
2223 }
2224
2225 armnn::IConnectableLayer* const layer = data.m_Network->AddDequantizeLayer();
2226 assert(layer != nullptr);
2227 input.Connect(layer->GetInputSlot(0));
2228
2229 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2230}
2231
2232template<typename HalPolicy,
2233 typename Operation = typename HalPolicy::Operation,
2234 typename Model = typename HalPolicy::Model>
2235bool ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
2236{
2237 using Operand = typename HalPolicy::Operand;
2238
2239 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2240 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
2241
2242 if (!input0.IsValid() || !input1.IsValid())
2243 {
2244 return Fail("%s: Operation has invalid inputs", __func__);
2245 }
2246
2247 // The FuseActivation parameter is always the input index 2
2248 // and it should be optional
2249 ActivationFn activationFunction;
2250 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2251 {
2252 return Fail("%s: Operation has invalid inputs", __func__);
2253 }
2254
2255 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2256 if (!output)
2257 {
2258 return Fail("%s: Could not read output 0", __func__);
2259 }
2260
2261 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2262 if (IsDynamicTensor(outputInfo))
2263 {
2264 return Fail("%s: Dynamic output tensors are not supported", __func__);
2265 }
2266
2267 bool isSupported = false;
2268 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2269 IsDivisionSupported,
2270 data.m_Backends,
2271 isSupported,
2272 input0.GetTensorInfo(),
2273 input1.GetTensorInfo(),
2274 outputInfo);
2275 if (!isSupported)
2276 {
2277 return false;
2278 }
2279
2280 armnn::IConnectableLayer* const startLayer = data.m_Network->AddDivisionLayer();
2281 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2282
2283 if (endLayer)
2284 {
Sadik Armagan64b19b52019-08-19 09:49:58 +01002285 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
2286 if (!isReshapeSupported)
2287 {
2288 return false;
2289 }
2290
Mike Kelly46272802019-08-14 17:00:48 +01002291 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2292 }
2293 return Fail("%s: ProcessActivation failed", __func__);
2294}
2295
2296template<typename HalPolicy,
2297 typename Operation = typename HalPolicy::Operation,
2298 typename Model = typename HalPolicy::Model>
2299bool ConvertFloor(const Operation& operation, const Model& model, ConversionData& data)
2300{
2301 using Operand = typename HalPolicy::Operand;
2302
2303 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2304 if (!input.IsValid())
2305 {
2306 return Fail("%s: Operation has invalid inputs", __func__);
2307 }
2308
2309 const Operand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2310 if (!outputOperand)
2311 {
2312 return Fail("%s: Operation has invalid outputs", __func__);
2313 }
2314
2315 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2316 if (IsDynamicTensor(outputInfo))
2317 {
2318 return Fail("%s: Dynamic output tensors are not supported", __func__);
2319 }
2320
2321 bool isSupported = false;
2322 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2323 IsFloorSupported,
2324 data.m_Backends,
2325 isSupported,
2326 input.GetTensorInfo(),
2327 outputInfo);
2328 if (!isSupported)
2329 {
2330 return false;
2331 }
2332
2333 armnn::IConnectableLayer* layer = data.m_Network->AddFloorLayer();
2334 assert(layer != nullptr);
2335 input.Connect(layer->GetInputSlot(0));
2336
2337 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2338}
2339
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002340inline bool IsQSymm8(const V1_0::Operand&)
2341{
2342 return false;
2343}
2344
2345#ifdef ARMNN_ANDROID_NN_V1_2
2346
2347inline bool IsQSymm8(const V1_2::Operand& operand)
2348{
2349 return operand.type == V1_2::OperandType::TENSOR_QUANT8_SYMM;
2350}
2351
2352#endif
2353
2354template<typename HalPolicy,
2355 typename Operation = typename HalPolicy::Operation,
2356 typename Model = typename HalPolicy::Model>
2357std::tuple<std::unique_ptr<float[]>, size_t, armnn::TensorInfo>
2358DequantizeIfRequired(size_t operand_index, const Operation& operation, const Model& model, const ConversionData& data)
2359{
2360 using Operand = typename HalPolicy::Operand;
2361
2362 bool weights_constant = false;
2363 if (!(IsOperandConstant<HalPolicy>(operation, operand_index, model, weights_constant) && !weights_constant))
2364 {
2365 return { nullptr, 0, armnn::TensorInfo() };
2366 }
2367
2368 const size_t weightsInputIndex = operation.inputs[operand_index];
2369
2370 // The weights are a non const tensor, this indicates they might be the output of a dequantize op.
2371 // Iterate over the nodes and find the previous operation which should be DEQUANTIZE
2372 for (uint32_t operationIdx = 0; operationIdx < model.operations.size(); ++operationIdx)
2373 {
2374 const auto& operationIt = model.operations[operationIdx];
2375 size_t outOpIndex = weightsInputIndex + 1;
2376
2377 // Search for the DEQUANTIZE op which has the operand with index equal to operandIndex
2378 if (operationIt.type != HalPolicy::OperationType::DEQUANTIZE)
2379 {
2380 continue;
2381 }
2382
2383 for (size_t i = 0; outOpIndex != weightsInputIndex && i < operation.outputs.size(); ++i)
2384 {
2385 outOpIndex = operationIt.outputs[i];
2386 break;
2387 }
2388
2389 if (outOpIndex != weightsInputIndex)
2390 {
2391 break;
2392 }
2393
2394 const Operand* operand = GetInputOperand<HalPolicy>(operationIt, 0, model);
2395 BOOST_ASSERT(operand);
2396
2397 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(*operand);
2398 if (!IsQSymm8(*operand))
2399 {
2400 // Only supporting dequantize from QSYMM8 to FLOAT
2401 break;
2402 }
2403
2404 // Allocate a new buffer for the dequantized data and manually dequantize
2405 const void* startValue = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
2406 if (!startValue)
2407 {
2408 // Failed to get the operand address
2409 break;
2410 }
2411
2412 const uint8_t* quantizedBuffer = reinterpret_cast<const uint8_t*>(startValue);
2413 size_t dequantizedBufferLength = operand->location.length;
2414 const float quantizationScale = tensorInfo.GetQuantizationScale();
2415 auto dequantizedBuffer = std::make_unique<float[]>(dequantizedBufferLength + 1);
2416 for (size_t i = 0; i < dequantizedBufferLength; ++i)
2417 {
2418 float* dstPtr = dequantizedBuffer.get();
2419 BOOST_ASSERT(dstPtr);
2420 *dstPtr++ = quantizedBuffer[i] * quantizationScale;
2421 }
2422
2423 tensorInfo.SetDataType(armnn::DataType::Float32);
2424 return { std::move(dequantizedBuffer), dequantizedBufferLength * sizeof(float), std::move(tensorInfo) };
2425 }
2426
2427 return { nullptr, 0, armnn::TensorInfo() };
2428}
2429
2430template<typename HalPolicy,
2431 typename Operation = typename HalPolicy::Operation,
2432 typename Model = typename HalPolicy::Model>
2433ConstTensorPin DequantizeAndMakeConstTensorPin(const Operation& operation,
2434 const Model& model,
2435 const ConversionData& data,
2436 size_t operandIndex,
2437 bool optional = false)
2438{
2439 auto dequantized = DequantizeIfRequired<HalPolicy, Operation, Model>(operandIndex,operation, model, data);
2440 if (std::get<1>(dequantized) == 0 && optional)
2441 {
2442 // Optional tensor with no values is not really an error. Return it as invalid, but marked as optional
2443 return ConstTensorPin(true);
2444 }
2445
2446 return std::get<1>(dequantized) ?
2447 ConstTensorPin(std::get<2>(dequantized), std::get<0>(dequantized).get(),
2448 std::get<1>(dequantized), g_DontPermute):
2449 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, operandIndex, model, data);
2450}
2451
2452
Mike Kelly46272802019-08-14 17:00:48 +01002453template<typename HalPolicy,
2454 typename Operation = typename HalPolicy::Operation,
2455 typename Model = typename HalPolicy::Model>
2456bool ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data)
2457{
2458 using Operand = typename HalPolicy::Operand;
Mike Kelly46272802019-08-14 17:00:48 +01002459 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2460 if (!input.IsValid())
2461 {
2462 return Fail("%s: Operation has invalid inputs", __func__);
2463 }
2464
2465 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2466 if (!output)
2467 {
2468 return Fail("%s: Could not read output 0", __func__);
2469 }
2470
2471 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2472 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2473
2474 if (IsDynamicTensor(outputInfo))
2475 {
2476 return Fail("%s: Dynamic output tensors are not supported", __func__);
2477 }
2478
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002479 ConstTensorPin weightsPin = DequantizeAndMakeConstTensorPin<HalPolicy, Operation, Model>(operation, model, data, 1);
Mike Kelly46272802019-08-14 17:00:48 +01002480
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002481 ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data); // 1D
2482
2483 if (!weightsPin.IsValid())
Mike Kelly46272802019-08-14 17:00:48 +01002484 {
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002485 return Fail("%s: Operation has invalid weights", __func__);
2486 }
2487
2488 if (!biasPin.IsValid())
2489 {
2490 return Fail("%s: Operation has invalid bias", __func__);
Mike Kelly46272802019-08-14 17:00:48 +01002491 }
2492
2493 armnn::ConstTensor weights = weightsPin.GetConstTensor();
2494 armnn::ConstTensor bias = biasPin.GetConstTensor();
2495 armnn::TensorInfo reshapedInfo = inputInfo;
2496
2497 try
2498 {
2499 reshapedInfo.SetShape(FlattenFullyConnectedInput(inputInfo.GetShape(), weights.GetInfo().GetShape()));
Pablo Tellofb45e2f2019-10-18 16:51:57 +01002500 }
2501 catch (const std::exception& e)
2502 {
Mike Kelly46272802019-08-14 17:00:48 +01002503 return Fail("%s: %s", __func__, e.what());
2504 }
2505
2506 // ensuring that the bias value is within 1% of the weights input (small float differences can exist)
2507 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo);
2508
2509 ActivationFn activationFunction;
2510 if (!GetInputActivationFunction<HalPolicy>(operation, 3, activationFunction, model, data))
2511 {
2512 return Fail("%s: Operation has invalid inputs", __func__);
2513 }
2514
2515 armnn::FullyConnectedDescriptor desc;
2516 desc.m_TransposeWeightMatrix = true;
2517 desc.m_BiasEnabled = true;
2518
2519 bool isSupported = false;
2520 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2521 IsFullyConnectedSupported,
2522 data.m_Backends,
2523 isSupported,
2524 reshapedInfo,
2525 outputInfo,
2526 weights.GetInfo(),
2527 bias.GetInfo(),
2528 desc);
2529 if (!isSupported)
2530 {
2531 return false;
2532 }
2533
2534 armnn::IConnectableLayer* startLayer =
2535 data.m_Network->AddFullyConnectedLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
2536 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2537
2538 if (endLayer != nullptr)
2539 {
2540 if (inputInfo.GetNumDimensions() > 2U)
2541 {
2542 armnn::ReshapeDescriptor reshapeDescriptor;
2543 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
2544
2545 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
2546 assert(reshapeLayer != nullptr);
2547 input.Connect(reshapeLayer->GetInputSlot(0));
2548 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
2549 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
2550 }
2551 else
2552 {
2553 input.Connect(startLayer->GetInputSlot(0));
2554 }
2555
2556 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2557 }
2558 else
2559 {
2560 return Fail("%s: ProcessActivation failed", __func__);
2561 }
2562}
2563
2564template<typename HalPolicy,
2565 typename Operation = typename HalPolicy::Operation,
2566 typename Model = typename HalPolicy::Model>
2567bool ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data)
2568{
Mike Kelly999e2092019-08-15 10:46:46 +01002569 if (operation.inputs.size() != 1)
2570 {
2571 return Fail("%s: Optional inputs are not supported", __func__);
2572 }
2573
Mike Kelly46272802019-08-14 17:00:48 +01002574 using Operand = typename HalPolicy::Operand;
2575
2576 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2577 if (!input.IsValid())
2578 {
2579 return Fail("%s: Operation has invalid inputs", __func__);
2580 }
2581
2582 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2583 if (!output)
2584 {
2585 return Fail("%s: Could not read output 0", __func__);
2586 }
2587
2588 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2589 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2590
2591 if (IsDynamicTensor(outputInfo))
2592 {
2593 return Fail("%s: Dynamic output tensors are not supported", __func__);
2594 }
2595 if (outputInfo.GetNumDimensions() != 4u)
2596 {
2597 return Fail("%s: Tensor Rank other than 4 is not supported", __func__);
2598 }
2599
2600 armnn::L2NormalizationDescriptor desc;
2601 desc.m_DataLayout = armnn::DataLayout::NHWC;
2602
2603 bool isSupported = false;
2604 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2605 IsL2NormalizationSupported,
2606 data.m_Backends,
2607 isSupported,
2608 inputInfo,
2609 outputInfo,
2610 desc);
2611 if (!isSupported)
2612 {
2613 return false;
2614 }
2615
2616 armnn::IConnectableLayer* layer = data.m_Network->AddL2NormalizationLayer(desc);
2617 assert(layer != nullptr);
2618 input.Connect(layer->GetInputSlot(0));
2619
2620 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2621}
2622
2623template<typename HalPolicy,
2624 typename Operation = typename HalPolicy::Operation,
2625 typename Model = typename HalPolicy::Model>
2626bool ConvertLocalResponseNormalization(const Operation& operation,
2627 const Model& model,
2628 ConversionData& data)
2629{
Mike Kelly999e2092019-08-15 10:46:46 +01002630 if (operation.inputs.size() != 5)
2631 {
2632 return Fail("%s: Optional inputs are not supported", __func__);
2633 }
2634
Mike Kelly46272802019-08-14 17:00:48 +01002635 using Operand = typename HalPolicy::Operand;
2636 using OperandType = typename HalPolicy::OperandType;
2637
2638 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2639 if (!input.IsValid())
2640 {
2641 return Fail("%s: Operation has invalid inputs", __func__);
2642 }
2643
2644 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2645 if (!output)
2646 {
2647 return Fail("%s: Could not read output 0", __func__);
2648 }
2649
2650 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2651 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2652
2653 if (IsDynamicTensor(outputInfo))
2654 {
2655 return Fail("%s: Dynamic output tensors are not supported", __func__);
2656 }
2657 if (outputInfo.GetNumDimensions() != 4u)
2658 {
2659 return Fail("%s: Tensor Rank other than 4 is not supported", __func__);
2660 }
2661
2662 armnn::NormalizationDescriptor descriptor;
2663 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
2664 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
2665 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
2666
2667 if (!input.IsValid() ||
2668 !GetInputScalar<HalPolicy>(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) ||
2669 !GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_K, model, data) ||
2670 !GetInputFloat32<HalPolicy>(operation, 3, descriptor.m_Alpha, model, data) ||
2671 !GetInputFloat32<HalPolicy>(operation, 4, descriptor.m_Beta, model, data))
2672 {
2673 return Fail("%s: Operation has invalid inputs", __func__);
2674 }
2675
2676 // ArmNN expects normSize to be the full size of the normalization
2677 // window rather than the radius as in AndroidNN.
2678 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
2679
2680 bool isSupported = false;
2681 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2682 IsNormalizationSupported,
2683 data.m_Backends,
2684 isSupported,
2685 inputInfo,
2686 outputInfo,
2687 descriptor);
2688 if (!isSupported)
2689 {
2690 return false;
2691 }
2692
2693
2694 armnn::IConnectableLayer* layer = data.m_Network->AddNormalizationLayer(descriptor);
2695 assert(layer != nullptr);
2696 input.Connect(layer->GetInputSlot(0));
2697
2698 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2699}
2700
2701template<typename HalPolicy,
2702 typename Operation = typename HalPolicy::Operation,
2703 typename Model = typename HalPolicy::Model>
2704bool ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data)
2705{
2706 using Operand = typename HalPolicy::Operand;
2707
2708 armnn::ActivationDescriptor desc;
2709 desc.m_Function = armnn::ActivationFunction::Sigmoid;
2710
2711 return ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
2712}
2713
2714template<typename HalPolicy,
2715 typename Operation = typename HalPolicy::Operation,
2716 typename Model = typename HalPolicy::Model>
2717bool ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
2718{
2719 using Operand = typename HalPolicy::Operand;
2720
2721 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2722 if (!input.IsValid())
2723 {
2724 return Fail("%s: Operation has invalid inputs", __func__);
2725 }
2726
2727 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2728 if (!output)
2729 {
2730 return Fail("%s: Could not read output 0", __func__);
2731 }
2732
2733 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2734 if (IsDynamicTensor(outputInfo))
2735 {
2736 return Fail("%s: Dynamic output tensors are not supported", __func__);
2737 }
2738
2739 const Operand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2740 if (!axisOperand)
2741 {
2742 return Fail("%s: Could not read input 1", __func__);
2743 }
2744
2745 std::vector<int32_t> axis;
2746 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2747 {
2748 return Fail("%s: Input 1 has invalid values", __func__);
2749 }
2750
2751 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2752
2753 // Convert the axis to unsigned int and remove duplicates.
2754 unsigned int rank = inputInfo.GetNumDimensions();
2755 std::set<unsigned int> uniqueAxis;
2756 std::transform(axis.begin(), axis.end(),
2757 std::inserter(uniqueAxis, uniqueAxis.begin()),
2758 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2759
2760 // Get the "keep dims" flag.
2761 int32_t keepDims = 0;
2762 if (!GetInputInt32<HalPolicy>(operation, 2, keepDims, model, data))
2763 {
2764 return Fail("%s: Could not read input 2", __func__);
2765 }
2766
2767 armnn::MeanDescriptor descriptor;
2768 descriptor.m_Axis.assign(uniqueAxis.begin(), uniqueAxis.end());
2769 descriptor.m_KeepDims = keepDims > 0;
2770
2771 bool isSupported = false;
2772 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2773 IsMeanSupported,
2774 data.m_Backends,
2775 isSupported,
2776 inputInfo,
2777 outputInfo,
2778 descriptor);
2779 if (!isSupported)
2780 {
2781 return false;
2782 }
2783
2784 armnn::IConnectableLayer* const layer = data.m_Network->AddMeanLayer(descriptor);
2785 assert(layer != nullptr);
2786 input.Connect(layer->GetInputSlot(0));
2787
2788 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2789}
2790
2791template<typename HalPolicy,
2792 typename Operation = typename HalPolicy::Operation,
2793 typename Model = typename HalPolicy::Model>
2794bool ConvertMul(const Operation& operation, const Model& model, ConversionData& data)
2795{
2796 using Operand = typename HalPolicy::Operand;
2797
2798 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2799 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
2800
2801 if (!input0.IsValid() || !input1.IsValid())
2802 {
2803 return Fail("%s: Operation has invalid inputs", __func__);
2804 }
2805
2806 // The FuseActivation parameter is always the input index 2
2807 // and it should be optional
2808 ActivationFn activationFunction;
2809 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
2810 {
2811 return Fail("%s: Operation has invalid inputs", __func__);
2812 }
2813
2814 const Operand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2815
2816 if (outputOperand == nullptr)
2817 {
2818 return false;
2819 }
2820
2821 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
2822 if (IsDynamicTensor(outputInfo))
2823 {
2824 return Fail("%s: Dynamic output tensors are not supported", __func__);
2825 }
2826
2827 bool isSupported = false;
2828 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2829 IsMultiplicationSupported,
2830 data.m_Backends,
2831 isSupported,
2832 input0.GetTensorInfo(),
2833 input1.GetTensorInfo(),
2834 outputInfo);
2835 if (!isSupported)
2836 {
2837 return false;
2838 }
2839
2840 armnn::IConnectableLayer* const startLayer = data.m_Network->AddMultiplicationLayer();
2841 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
2842
2843 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
2844 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
2845
2846 if (endLayer != nullptr)
2847 {
Sadik Armagan64b19b52019-08-19 09:49:58 +01002848 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
2849 if (!isReshapeSupported)
2850 {
2851 return false;
2852 }
2853
Mike Kelly46272802019-08-14 17:00:48 +01002854 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
2855 }
2856 else
2857 {
2858 return Fail("%s: ProcessActivation failed", __func__);
2859 }
2860}
2861
2862template<typename HalPolicy,
2863 typename Operation = typename HalPolicy::Operation,
2864 typename Model = typename HalPolicy::Model>
2865bool ConvertPad(Operation& operation, const Model& model, ConversionData& data)
2866{
2867 using Operand = typename HalPolicy::Operand;
2868
Mike Kelly3c673942019-07-25 09:26:06 +01002869 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2870 if (!input.IsValid())
2871 {
2872 return Fail("%s: Operation has invalid inputs", __func__);
2873 }
2874
2875 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2876 unsigned int rank = inputInfo.GetNumDimensions();
2877
2878 armnn::PadDescriptor descriptor;
2879 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
2880 {
2881 return Fail("%s: Could not convert paddings", __func__);
2882 }
2883
2884 // Before Android Q, the pad value for ANEURALNETWORKS_TENSOR_QUANT8_ASYMM was undefined. Since Android Q the pad
2885 // value must be "logical zero" we set it to be equal to the QuantizationOffset so effectively it ends up as
2886 // (QuantizationOffset - QuantizationOffset) * scale = 0.
2887 if (inputInfo.GetDataType() == armnn::DataType::QuantisedAsymm8)
2888 {
2889 descriptor.m_PadValue = inputInfo.GetQuantizationOffset();
2890 }
2891
Mike Kelly46272802019-08-14 17:00:48 +01002892 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kelly3c673942019-07-25 09:26:06 +01002893 if (!output)
2894 {
2895 return Fail("%s: Could not read output", __func__);
2896 }
2897
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002898 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Mike Kelly3c673942019-07-25 09:26:06 +01002899 if (IsDynamicTensor(outputInfo))
2900 {
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002901 return Fail("%s: Dynamic output tensors are not supported", __func__);
Mike Kelly3c673942019-07-25 09:26:06 +01002902 }
2903
2904 bool isSupported = false;
2905 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2906 IsPadSupported,
2907 data.m_Backends,
2908 isSupported,
2909 inputInfo,
2910 outputInfo,
2911 descriptor);
2912 if (!isSupported)
2913 {
2914 return false;
2915 }
2916
2917 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
2918 assert(layer != nullptr);
2919 input.Connect(layer->GetInputSlot(0));
2920 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
2921
Aron Virginas-Tarb7421e52019-07-26 13:14:39 +01002922 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
Mike Kelly3c673942019-07-25 09:26:06 +01002923}
2924
Mike Kelly0a879362019-07-29 16:56:31 +01002925template<typename HalPolicy,
2926 typename Operation = typename HalPolicy::Operation,
Mike Kelly46272802019-08-14 17:00:48 +01002927 typename Model = typename HalPolicy::Model>
2928bool ConvertReshape(const Operation& operation, const Model& model, ConversionData& data)
2929{
2930 using Operand = typename HalPolicy::Operand;
2931
2932 const Operand* inputOperand = GetInputOperand<HalPolicy>(operation, 0, model);
2933 const Operand* requestedShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2934 const Operand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2935
2936 if (inputOperand == nullptr
2937 || requestedShapeOperand == nullptr
2938 || outputOperand == nullptr)
2939 {
2940 return Fail("%s: Operation has invalid inputs", __func__);
2941 }
2942
2943 if (requestedShapeOperand->dimensions.size() != 1)
2944 {
2945 return Fail("%s: Input 1 expected to be one-dimensional (found %i dimensions)",
2946 __func__, requestedShapeOperand->dimensions.size());
2947 }
2948
2949 std::vector<int32_t> targetDimensions;
2950 if (!GetTensorInt32Values<HalPolicy>(*requestedShapeOperand, targetDimensions, model, data))
2951 {
2952 return Fail("%s: Could not read values of input 1", __func__);
2953 }
2954
2955 const Shape inputOperandShape = GetOperandShape(*inputOperand);
2956
2957 Shape requestedShape;
2958 // targetDimensions may contain special values (e.g. -1). reshapePrepare() is an AndroidNN provided utility
2959 // function that resolves these values into a fully specified tensor shape.
2960 if (!reshapePrepare(inputOperandShape, targetDimensions.data(), targetDimensions.size(), &requestedShape))
2961 {
2962 return Fail("%s: Failed to resolve the requested shape", __func__);
2963 }
2964
2965 const Shape outputOperandShape = GetOperandShape(*outputOperand);
2966 if (!SameShape(requestedShape, outputOperandShape))
2967 {
2968 return Fail("%s: Shape of output operand does not match resolved requested shape", __func__);
2969 }
2970
2971 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2972 if (!input.IsValid())
2973 {
2974 return Fail("%s: Could not read input 0", __func__);
2975 }
2976
2977 armnn::ReshapeDescriptor reshapeDescriptor;
2978 reshapeDescriptor.m_TargetShape = armnn::TensorShape(requestedShape.dimensions.size(),
2979 requestedShape.dimensions.data());
2980
2981 bool isSupported = false;
2982 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2983 IsReshapeSupported,
2984 data.m_Backends,
2985 isSupported,
2986 input.GetTensorInfo(),
2987 reshapeDescriptor);
2988 if (!isSupported)
2989 {
2990 return false;
2991 }
2992
2993 armnn::IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
2994 assert(layer != nullptr);
2995 input.Connect(layer->GetInputSlot(0));
2996
2997 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
2998}
2999
3000template<typename HalPolicy,
3001 typename Operation = typename HalPolicy::Operation,
Mike Kelly0a879362019-07-29 16:56:31 +01003002 typename Model = typename HalPolicy::Model>
3003bool ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
3004{
Mike Kelly46272802019-08-14 17:00:48 +01003005 using Operand = typename HalPolicy::Operand;
3006
Mike Kelly0a879362019-07-29 16:56:31 +01003007 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3008 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
3009
3010 if (!input0.IsValid() || !input1.IsValid())
3011 {
3012 return Fail("%s: Operation has invalid inputs", __func__);
3013 }
3014
3015 // The FuseActivation parameter is always the input index 2
3016 // and it should be optional
3017 ActivationFn activationFunction;
3018 if (!GetOptionalInputActivation<HalPolicy>(operation, 2, activationFunction, model, data))
3019 {
3020 return Fail("%s: Operation has invalid inputs", __func__);
3021 }
3022
3023 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3024 if (!output)
3025 {
3026 return Fail("%s: Could not read output 0", __func__);
3027 }
3028
3029 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3030 if (IsDynamicTensor(outputInfo))
3031 {
3032 return Fail("%s: Dynamic output tensors are not supported", __func__);
3033 }
3034
3035 bool isSupported = false;
3036 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3037 IsSubtractionSupported,
3038 data.m_Backends,
3039 isSupported,
3040 input0.GetTensorInfo(),
3041 input1.GetTensorInfo(),
3042 outputInfo);
3043 if (!isSupported)
3044 {
3045 return false;
3046 }
3047
3048 armnn::IConnectableLayer* const startLayer = data.m_Network->AddSubtractionLayer();
3049 armnn::IConnectableLayer* const endLayer = ProcessActivation(outputInfo, activationFunction, startLayer, data);
3050
3051 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
3052 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
3053
3054 if (endLayer)
3055 {
Sadik Armagan64b19b52019-08-19 09:49:58 +01003056 bool isReshapeSupported = BroadcastTensor(input0, input1, startLayer, data);
3057 if (!isReshapeSupported)
3058 {
3059 return false;
3060 }
Mike Kelly0a879362019-07-29 16:56:31 +01003061 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
3062 }
3063
3064 return Fail("%s: ProcessActivation failed", __func__);
3065}
3066
Finn Williams23b87b32019-07-30 11:44:05 +01003067template<typename HalPolicy,
Mike Kelly46272802019-08-14 17:00:48 +01003068 typename Operation = typename HalPolicy::Operation,
3069 typename Model = typename HalPolicy::Model>
3070bool ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
3071{
3072 using Operand = typename HalPolicy::Operand;
3073
3074 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3075 if (!input.IsValid())
3076 {
3077 return Fail("%s: Operation has invalid inputs", __func__);
3078 }
3079
3080 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3081 unsigned int rank = inputInfo.GetNumDimensions();
3082 if (rank > 4)
3083 {
3084 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3085 }
3086
3087 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3088 if (!output)
3089 {
3090 return Fail("%s: Could not read output 0", __func__);
3091 }
3092
3093 if (IsDynamicTensor(GetTensorInfoForOperand(*output)))
3094 {
3095 return Fail("%s: Dynamic output tensors are not supported", __func__);
3096 }
3097
3098 // NOTE: Axis is an optional parameter to SQUEEZE, therefore we do not want to generate a failure
3099 // if the operand index is out of bounds.
3100 const Operand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model, false);
3101
3102 const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
3103
3104 std::vector<int32_t> axis;
3105 if (!axisOperand)
3106 {
3107 axis.assign(dimensionSequence,
3108 dimensionSequence + rank);
3109 }
3110 else
3111 {
3112 GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data);
3113 }
3114
3115 std::vector<uint32_t> outputDims;
3116 for (unsigned int i = 0; i < rank; i++)
3117 {
3118 bool skipSqueeze = (std::find(axis.begin(), axis.end(), i) == axis.end());
3119 auto currentDimension = inputInfo.GetShape()[i];
3120 if (skipSqueeze || currentDimension != 1)
3121 {
3122 outputDims.push_back(currentDimension);
3123 }
3124 }
3125
3126 armnn::TensorShape outShape = armnn::TensorShape(outputDims.size(), outputDims.data());
3127
3128 armnn::TensorInfo outputInfo = inputInfo;
3129 outputInfo.SetShape(outShape);
3130
3131 armnn::ReshapeDescriptor reshapeDesc;
3132 reshapeDesc.m_TargetShape = outputInfo.GetShape();
3133
3134 bool isSupported = false;
3135 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3136 IsReshapeSupported,
3137 data.m_Backends,
3138 isSupported,
3139 inputInfo,
3140 reshapeDesc);
3141 if (!isSupported)
3142 {
3143 return false;
3144 }
3145
3146 armnn::IConnectableLayer* const layer = data.m_Network->AddReshapeLayer(reshapeDesc);
3147 assert(layer != nullptr);
3148 input.Connect(layer->GetInputSlot(0));
3149
3150 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3151}
3152
3153template<typename HalPolicy,
3154 typename Operation = typename HalPolicy::Operation,
3155 typename Model = typename HalPolicy::Model>
3156bool ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data)
3157{
3158 using Operand = typename HalPolicy::Operand;
3159
3160 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3161 if (!input.IsValid())
3162 {
3163 return Fail("%s: Operation has invalid inputs", __func__);
3164 }
3165
3166 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3167 unsigned int rank = inputInfo.GetNumDimensions();
3168 if (rank > 4)
3169 {
3170 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3171 }
3172
3173 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3174 if (!output)
3175 {
3176 return Fail("%s: Could not read output 0", __func__);
3177 }
3178
3179 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3180 if (IsDynamicTensor(outputInfo))
3181 {
3182 return Fail("%s: Dynamic output tensors are not supported", __func__);
3183 }
3184
3185 const Operand* beginOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3186 const Operand* endOperand = GetInputOperand<HalPolicy>(operation, 2, model);
3187 const Operand* stridesOperand = GetInputOperand<HalPolicy>(operation, 3, model);
3188
3189 std::vector<int32_t> beginValues;
3190 std::vector<int32_t> endValues;
3191 std::vector<int32_t> stridesValues;
3192
3193 // The length of the beginOperand, endOperand and stridesOperand must be of a rank(input)
3194 auto ValidateInputOperands = [&] (const Operand& operand, std::vector<int32_t>& operandValues)
3195 {
3196 if (!GetTensorInt32Values<HalPolicy>(operand, operandValues, model, data))
3197 {
3198 return false;
3199 }
3200
3201 if (operandValues.size() != rank)
3202 {
3203 return false;
3204 }
3205
3206 return true;
3207 };
3208
3209 if (!ValidateInputOperands(*beginOperand, beginValues)
3210 || !ValidateInputOperands(*endOperand, endValues)
3211 || !ValidateInputOperands(*stridesOperand, stridesValues))
3212 {
3213 return Fail("%s: Operation has invalid input operand", __func__);
3214 }
3215
3216 // Stride cannot have value '0'
3217 if (std::any_of(stridesValues.cbegin(), stridesValues.cend(), [](int32_t i){ return i == 0; }))
3218 {
3219 return Fail("%s: Stride must be non-zero value.", __func__);
3220 }
3221
3222 armnn::StridedSliceDescriptor descriptor;
3223 descriptor.m_Begin.assign(beginValues.cbegin(), beginValues.cend());
3224 descriptor.m_End.assign(endValues.cbegin(), endValues.cend());
3225 descriptor.m_Stride.assign(stridesValues.cbegin(), stridesValues.cend());
3226 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
3227
3228 // Get the "begin_mask", "end_mask", and "shrink_axis_mask" flags
3229 if (!GetInputInt32<HalPolicy>(operation, 4, descriptor.m_BeginMask, model, data) ||
3230 !GetInputInt32<HalPolicy>(operation, 5, descriptor.m_EndMask, model, data) ||
3231 !GetInputInt32<HalPolicy>(operation, 6, descriptor.m_ShrinkAxisMask, model, data))
3232 {
3233 return Fail("%s: Operation has invalid inputs", __func__);
3234 }
3235
3236 bool isSupported = false;
3237 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3238 IsStridedSliceSupported,
3239 data.m_Backends,
3240 isSupported,
3241 inputInfo,
3242 outputInfo,
3243 descriptor);
3244 if (!isSupported)
3245 {
3246 return false;
3247 }
3248
3249 armnn::IConnectableLayer* const layer = data.m_Network->AddStridedSliceLayer(descriptor);
3250 assert(layer != nullptr);
3251 input.Connect(layer->GetInputSlot(0));
3252
3253 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3254}
3255
3256template<typename HalPolicy,
3257 typename Operation = typename HalPolicy::Operation,
3258 typename Model = typename HalPolicy::Model>
3259bool ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
3260{
3261 using Operand = typename HalPolicy::Operand;
3262
3263 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3264 if (!input.IsValid())
3265 {
3266 return Fail("%s: Operation has invalid inputs", __func__);
3267 }
3268
3269 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3270 unsigned int rank = inputInfo.GetNumDimensions();
3271 if (rank > 4)
3272 {
3273 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
3274 }
3275
3276 // NOTE: Axis is an optional parameter to TRANSPOSE, therefore we do not want to generate a failure
3277 // if the operand index is out of bounds.
3278 const Operand* permOperand = GetInputOperand<HalPolicy>(operation, 1, model, false);
3279
3280 std::vector<int32_t> perm(rank);
3281 if (!permOperand)
3282 {
3283 // NOTE: If perm is not given, it is set to (n-1...0), where n is the rank of the tensor
3284 for (unsigned int i = rank; i > 0; i--)
3285 {
3286 perm[rank - i] = boost::numeric_cast<int> (i - 1);
3287 }
3288 }
3289 else
3290 {
3291 GetTensorInt32Values<HalPolicy>(*permOperand, perm, model, data);
3292 }
3293
3294 std::vector<uint32_t> outputDims(perm.begin(), perm.begin() + rank);
3295
3296 auto permutationVector = armnn::PermutationVector(outputDims.data(), outputDims.size());
3297 if (!permutationVector.IsEqual(NHWCToArmNN)
3298 && !permutationVector.IsEqual(ArmNNToNHWC)
3299 && !permutationVector.IsEqual({ 3, 2, 0, 1 }))
3300 {
3301 return Fail("%s: Only [0, 3, 1, 2], [0, 2, 3, 1] and [3, 2, 0, 1] permutations are supported.", __func__);
3302 }
3303
3304 armnn::PermuteDescriptor permuteDesc;
3305 permuteDesc.m_DimMappings = permutationVector;
3306
3307 const Operand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3308 if (!output)
3309 {
3310 return Fail("%s: Could not read output 0", __func__);
3311 }
3312
3313 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3314
3315 bool isSupported = false;
3316 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3317 IsPermuteSupported,
3318 data.m_Backends,
3319 isSupported,
3320 inputInfo,
3321 outputInfo,
3322 permuteDesc);
3323 if (!isSupported)
3324 {
3325 return false;
3326 }
3327
3328 armnn::IConnectableLayer* const layer = data.m_Network->AddPermuteLayer(permuteDesc);
3329 assert(layer != nullptr);
3330 input.Connect(layer->GetInputSlot(0));
3331
3332 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3333}
3334
3335template<typename HalPolicy,
Finn Williams23b87b32019-07-30 11:44:05 +01003336 typename HalOperation = typename HalPolicy::Operation,
Finn Williams0e4e4392019-07-31 10:56:27 +01003337 typename HalOperand = typename HalPolicy::Operand,
Finn Williams23b87b32019-07-30 11:44:05 +01003338 typename HalModel = typename HalPolicy::Model>
3339bool ConvertBatchToSpaceNd(const HalOperation& operation,
3340 const HalModel& model,
3341 ConversionData& data)
3342{
Finn Williams23b87b32019-07-30 11:44:05 +01003343
3344 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3345 if (!input.IsValid())
3346 {
3347 return Fail("%s: Operation has invalid inputs", __func__);
3348 }
3349
3350 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3351 if (!output)
3352 {
3353 return Fail("%s: Could not read output 0", __func__);
3354 }
3355
3356 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3357 if (IsDynamicTensor(outputInfo))
3358 {
3359 return Fail("%s: Dynamic output tensors are not supported", __func__);
3360 }
3361
3362 const HalOperand* blockOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3363 if (!blockOperand)
3364 {
3365 return Fail("%s: Could not read input 1", __func__);
3366 }
3367
3368 // Convert the block operand to int32
3369 std::vector<int32_t> block;
3370 if (!GetTensorInt32Values<HalPolicy>(*blockOperand, block, model, data))
3371 {
3372 return Fail("%s: Input 1 has invalid values", __func__);
3373 }
3374
3375 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3376
3377 unsigned int rank = inputInfo.GetNumDimensions();
3378 if (rank != 4)
3379 {
3380 Fail("%s: Only inputs with rank equal to 4 are supported", __func__);
3381 }
3382
3383 if (std::any_of(block.cbegin(), block.cend(), [](int32_t i){ return i < 1; }))
3384 {
3385 return Fail("%s: Block sizes for each spatial dimension of the input tensor must be"
3386 " greater than or equal to 1", __func__);
3387 }
3388
3389 armnn::BatchToSpaceNdDescriptor batchToSpaceNdDesc;
3390 batchToSpaceNdDesc.m_BlockShape.assign(block.cbegin(), block.cend());
3391 batchToSpaceNdDesc.m_DataLayout = armnn::DataLayout::NHWC;
3392
3393 if (Is12Operand(*output))
3394 {
Finn Williams0e4e4392019-07-31 10:56:27 +01003395 batchToSpaceNdDesc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
Finn Williams23b87b32019-07-30 11:44:05 +01003396 }
3397 // Setting crops to 0,0 0,0 as it is not supported in Android NN API
3398 batchToSpaceNdDesc.m_Crops = {{0, 0}, {0, 0}};
3399
3400 bool isSupported = false;
3401 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3402 IsBatchToSpaceNdSupported,
3403 data.m_Backends,
3404 isSupported,
3405 inputInfo,
3406 outputInfo,
3407 batchToSpaceNdDesc);
3408 if (!isSupported)
3409 {
3410 return false;
3411 }
3412
3413 armnn::IConnectableLayer* const layer = data.m_Network->AddBatchToSpaceNdLayer(batchToSpaceNdDesc);
3414 assert(layer != nullptr);
3415 input.Connect(layer->GetInputSlot(0));
3416
3417 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3418}
Mike Kelly0a879362019-07-29 16:56:31 +01003419
Finn Williamsd74c5052019-07-30 17:06:00 +01003420template<typename HalPolicy,
3421 typename HalOperation = typename HalPolicy::Operation,
3422 typename HalOperand = typename HalPolicy::Operand,
3423 typename HalModel = typename HalPolicy::Model>
3424bool ConvertSpaceToBatchNd(const HalOperation& operation, const HalModel& model, ConversionData& data)
3425{
3426 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3427 if (!input.IsValid())
3428 {
3429 return Fail("%s: Operation has invalid inputs", __func__);
3430 }
3431
3432 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
3433 unsigned int rank = inputInfo.GetNumDimensions();
3434 unsigned int spatialDim = rank - 2;
3435
3436 if (rank != 4)
3437 {
3438 Fail("%s: Only inputs with rank 4 are supported", __func__);
3439 }
3440
3441 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3442 if (!output)
3443 {
3444 return Fail("%s: Could not read output 0", __func__);
3445 }
3446
3447 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3448 if (IsDynamicTensor(outputInfo))
3449 {
3450 return Fail("%s: Dynamic output tensors are not supported", __func__);
3451 }
3452
3453 const HalOperand* blockShapeOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3454 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 2, model);
3455
3456 armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand(*blockShapeOperand);
3457 if (blockShapeOperandShape.GetNumDimensions() != 1 || blockShapeOperandShape.GetNumElements() != spatialDim)
3458 {
3459 return Fail("%s: Operation has invalid block shape operand: expected shape [%d]", __func__, spatialDim);
3460 }
3461
3462 std::vector<int32_t> blockShape;
3463 GetTensorInt32Values<HalPolicy>(*blockShapeOperand, blockShape, model, data);
3464 if (std::any_of(blockShape.cbegin(), blockShape.cend(), [](int32_t i){ return i < 1; }))
3465 {
3466 return Fail("%s: Block shape must be at least 1 in all dimensions.", __func__);
3467 }
3468
3469 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
3470 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != 2 * spatialDim)
3471 {
3472 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, spatialDim);
3473 }
3474
3475 std::vector<std::pair<unsigned int, unsigned int>> paddingList;
3476 std::vector<int32_t> paddings;
3477 GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data);
3478 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
3479 {
3480 int paddingBeforeInput = paddings[i];
3481 int paddingAfterInput = paddings[i + 1];
3482 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
3483 {
3484 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
3485 }
3486
3487 paddingList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
3488 }
3489
3490 armnn::SpaceToBatchNdDescriptor descriptor;
3491 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
3492 descriptor.m_BlockShape.assign(blockShape.cbegin(), blockShape.cend());
3493 descriptor.m_PadList.assign(paddingList.cbegin(), paddingList.cend());
3494
3495 if (Is12Operand(*output))
3496 {
3497 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
3498 }
3499
3500 bool isSupported = false;
3501 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3502 IsSpaceToBatchNdSupported,
3503 data.m_Backends,
3504 isSupported,
3505 inputInfo,
3506 outputInfo,
3507 descriptor);
3508 if (!isSupported)
3509 {
3510 return false;
3511 }
3512
3513 armnn::IConnectableLayer* const layer = data.m_Network->AddSpaceToBatchNdLayer(descriptor);
3514 assert(layer != nullptr);
3515 input.Connect(layer->GetInputSlot(0));
3516
3517 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3518}
3519
Kevin May407718f2019-09-09 14:46:41 +01003520template<typename HalPolicy,
3521 typename HalOperation = typename HalPolicy::Operation,
3522 typename HalModel = typename HalPolicy::Model>
3523bool ConvertAbs(const HalOperation& operation, const HalModel& model, ConversionData& data)
3524{
3525 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3526
3527 if (!input.IsValid())
3528 {
3529 return Fail("%s: Operation has invalid input", __func__);
3530 }
3531
3532 using HalOperand = typename HalPolicy::Operand;
3533 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3534 if (!output)
3535 {
3536 return Fail("%s: Could not read output 0", __func__);
3537 }
3538
3539 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3540 if (IsDynamicTensor(outputInfo))
3541 {
3542 return Fail("%s: Dynamic output tensors are not supported", __func__);
3543 }
3544
3545 bool isSupported = false;
3546 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3547 IsAbsSupported,
3548 data.m_Backends,
3549 isSupported,
3550 input.GetTensorInfo(),
3551 outputInfo);
3552
3553 if (!isSupported)
3554 {
3555 return false;
3556 }
3557
3558 armnn::IConnectableLayer* const layer = data.m_Network->AddAbsLayer();
3559 assert(layer != nullptr);
3560 input.Connect(layer->GetInputSlot(0));
3561
3562 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
3563}
3564
3565
saoste01b8471482018-10-10 09:44:51 +01003566} // namespace armnn_driver