xref: /aosp_15_r20/external/armnn/shim/sl/canonical/ConversionUtils.hpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1 //
2 // Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #pragma once
7 
8 #include "CanonicalUtils.hpp"
9 
10 #include <armnn/ArmNN.hpp>
11 #include <armnn/BackendHelper.hpp>
12 #include <armnn/utility/Assert.hpp>
13 #include <armnn/utility/IgnoreUnused.hpp>
14 #include <armnn/utility/NumericCast.hpp>
15 
16 #include <armnnUtils/DataLayoutIndexed.hpp>
17 #include <armnnUtils/Transpose.hpp>
18 
19 #include <ActivationFunctor.h>
20 #include <CpuExecutor.h>
21 #include <OperationsUtils.h>
22 
23 #include <armnnUtils/FloatingPointComparison.hpp>
24 
25 #include <log/log.h>
26 #include <vector>
27 
getMainModel(const android::nn::Model & model)28 inline const android::nn::Model::Subgraph& getMainModel(const android::nn::Model& model) { return model.main; }
29 
30 namespace armnn_driver
31 {
32 
33 ///
34 /// Helper classes
35 ///
36 
37 #include <nnapi/OperandTypes.h>
38 #include <nnapi/Result.h>
39 #include <nnapi/TypeUtils.h>
40 #include <nnapi/Types.h>
41 #include <nnapi/Validation.h>
42 
43 using Model                     = ::android::nn::Model;
44 using Operand                   = ::android::nn::Operand;
45 using OperandLifeTime           = ::android::nn::Operand::LifeTime;
46 using OperandType               = ::android::nn::OperandType;
47 using Operation                 = ::android::nn::Operation;
48 using OperationType             = ::android::nn::OperationType;
49 using ErrorStatus               = ::android::nn::ErrorStatus;
50 
51 struct ConversionData
52 {
ConversionDataarmnn_driver::ConversionData53     ConversionData(const std::vector<armnn::BackendId>& backends)
54     : m_Backends(backends)
55     , m_Network(nullptr, nullptr)
56     , m_DynamicInputsEncountered(false)
57     {}
58 
59     const std::vector<armnn::BackendId>       m_Backends;
60     armnn::INetworkPtr                        m_Network;
61     std::vector<armnn::IOutputSlot*>          m_OutputSlotForOperand;
62     std::vector<::android::nn::RunTimePoolInfo> m_MemPools;
63     bool m_DynamicInputsEncountered;
64 };
65 
66 class LayerInputHandle
67 {
68 public:
69     LayerInputHandle();
70     LayerInputHandle(bool valid, armnn::IOutputSlot* outputSlot, armnn::TensorInfo tensorInfo);
71 
72     bool IsValid() const;
73 
74     void Connect(armnn::IInputSlot& inputSlot);
75 
76     void Disconnect(armnn::IInputSlot& inputSlot);
77 
78     const armnn::TensorInfo& GetTensorInfo() const;
79 
80     void SanitizeQuantizationScale(LayerInputHandle& weight, LayerInputHandle& input);
81 
82     armnn::IOutputSlot* GetOutputSlot() const;
83 
84 private:
85     armnn::IOutputSlot* m_OutputSlot;
86     bool                m_Valid;
87     armnn::TensorInfo   m_TensorInfo;
88 };
89 
90 class ConstTensorPin
91 {
92 public:
93     // Creates an invalid tensor pin (can be used to signal errors)
94     // The optional flag can be set to indicate the tensor values were missing, but it was otherwise valid
95     ConstTensorPin(bool optional = false);
96 
97     // @param tensorInfo TensorInfo associated with the tensor.
98     // @param valueStart Start address of tensor data. Belongs to one of the memory pools associated with
99     // the model being converted.
100     // @param numBytes Number of bytes for the tensor data.
101     ConstTensorPin(armnn::TensorInfo& tensorInfo, const void* valueStart, uint32_t numBytes,
102                    const armnn::PermutationVector& mappings);
103 
104     ConstTensorPin(const ConstTensorPin& other) = delete;
105     ConstTensorPin(ConstTensorPin&& other)      = default;
106 
107     bool IsValid() const;
108     bool IsOptional() const;
109 
110     const armnn::ConstTensor& GetConstTensor() const;
111     const armnn::ConstTensor* GetConstTensorPtr() const;
112 
113 private:
114     armnn::ConstTensor m_ConstTensor;
115 
116     // Owned memory for swizzled tensor data, only required if the tensor needed
117     // swizzling. Otherwise, @ref m_ConstTensor will reference memory from one of
118     // the pools associated with the model being converted.
119     std::vector<uint8_t> m_SwizzledTensorData;
120 
121     // optional flag to indicate that an invalid tensor pin is not an error, but the optional values were not given
122     bool m_Optional;
123 };
124 
125 enum class ConversionResult
126 {
127     Success,
128     ErrorMappingPools,
129     UnsupportedFeature
130 };
131 
132 } // namespace armnn_driver
133 
134 ///
135 /// Utility functions
136 ///
137 
138 namespace
139 {
140 using namespace armnn_driver;
141 
142 // Convenience function to log the reason for failing to convert a model.
143 // @return Always returns false (so that it can be used by callers as a quick way to signal an error and return)
144 template<class... Args>
Fail(const char * formatStr,Args &&...args)145 static bool Fail(const char* formatStr, Args&&... args)
146 {
147     ALOGD(formatStr, std::forward<Args>(args)...);
148     return false;
149 }
150 
151 // Convenience macro to call an Is*Supported function and log caller name together with reason for lack of support.
152 // Called as: FORWARD_LAYER_SUPPORT_FUNC(__func__, Is*Supported, backends, a, b, c, d, e)
153 #define FORWARD_LAYER_SUPPORT_FUNC(funcName, func, backends, supported, setBackend, ...) \
154 try \
155 { \
156     for (auto&& backendId : backends) \
157     { \
158         auto layerSupportObject = armnn::GetILayerSupportByBackendId(backendId); \
159         if (layerSupportObject.IsBackendRegistered()) \
160         { \
161             std::string reasonIfUnsupported; \
162             supported = \
163                 layerSupportObject.func(__VA_ARGS__, armnn::Optional<std::string&>(reasonIfUnsupported)); \
164             if (supported) \
165             { \
166                 setBackend = backendId; \
167                 break; \
168             } \
169             else \
170             { \
171                 if (reasonIfUnsupported.size() > 0) \
172                 { \
173                     VLOG(DRIVER) << funcName << ": not supported by armnn: " <<  reasonIfUnsupported.c_str(); \
174                 } \
175                 else \
176                 { \
177                     VLOG(DRIVER) << funcName << ": not supported by armnn"; \
178                 } \
179             } \
180         } \
181         else \
182         { \
183             VLOG(DRIVER) << funcName << ": backend not registered: " << backendId.Get().c_str(); \
184         } \
185     } \
186     if (!supported) \
187     { \
188         VLOG(DRIVER) << funcName << ": not supported by any specified backend"; \
189     } \
190 } \
191 catch (const armnn::InvalidArgumentException &e) \
192 { \
193     throw armnn::InvalidArgumentException(e, "Failed to check layer support", CHECK_LOCATION()); \
194 }
195 
GetTensorShapeForOperand(const Operand & operand)196 inline armnn::TensorShape GetTensorShapeForOperand(const Operand& operand)
197 {
198     return armnn::TensorShape(operand.dimensions.size(), operand.dimensions.data());
199 }
200 
201 // Support within the 1.3 driver for specific tensor data types
IsOperandTypeSupportedForTensors(OperandType type)202 inline bool IsOperandTypeSupportedForTensors(OperandType type)
203 {
204     return type == OperandType::BOOL                           ||
205            type == OperandType::TENSOR_BOOL8                   ||
206            type == OperandType::TENSOR_FLOAT16                 ||
207            type == OperandType::TENSOR_FLOAT32                 ||
208            type == OperandType::TENSOR_QUANT8_ASYMM            ||
209            type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED     ||
210            type == OperandType::TENSOR_QUANT8_SYMM             ||
211            type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
212            type == OperandType::TENSOR_QUANT16_SYMM            ||
213            type == OperandType::TENSOR_INT32;
214 }
215 
IsBool(Operand operand)216 inline bool IsBool(Operand operand)
217 {
218     return operand.type == OperandType::BOOL;
219 }
220 
Is12OrLaterOperand(Operand)221 inline bool Is12OrLaterOperand(Operand)
222 {
223     return true;
224 }
225 
226 
227 template<typename LayerHandleType>
AddReshapeLayer(armnn::INetwork & network,LayerHandleType & inputLayer,armnn::TensorInfo reshapeInfo)228 armnn::IConnectableLayer& AddReshapeLayer(armnn::INetwork& network,
229                                           LayerHandleType& inputLayer,
230                                           armnn::TensorInfo reshapeInfo)
231 {
232     armnn::ReshapeDescriptor reshapeDescriptor;
233     reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
234 
235     armnn::IConnectableLayer* reshapeLayer = network.AddReshapeLayer(reshapeDescriptor);
236     ARMNN_ASSERT(reshapeLayer != nullptr);
237 
238     // Attach the input layer to the reshape layer
239     inputLayer.Connect(reshapeLayer->GetInputSlot(0));
240     reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapeInfo);
241 
242     return *reshapeLayer;
243 }
244 
245 
FlattenFullyConnectedInput(const armnn::TensorShape & inputShape,const armnn::TensorShape & weightsShape)246  armnn::TensorShape FlattenFullyConnectedInput(const armnn::TensorShape& inputShape,
247                                                const armnn::TensorShape& weightsShape)
248 {
249     if (inputShape.GetNumDimensions() > 2U)
250     {
251         unsigned int totalInputElements = inputShape.GetNumElements();
252         unsigned int inputSize = weightsShape[1];
253 
254         unsigned int batchSize = totalInputElements / inputSize;
255 
256         if(totalInputElements % batchSize != 0)
257         {
258             throw std::runtime_error("Failed to deduce tensor shape");
259         }
260 
261         return armnn::TensorShape({batchSize, inputSize});
262     }
263     else
264     {
265         return inputShape;
266     }
267 }
268 
VerifyFullyConnectedShapes(const armnn::TensorShape & inputShape,const armnn::TensorShape & weightsShape,const armnn::TensorShape & outputShape,bool transposeWeightMatrix)269 inline bool VerifyFullyConnectedShapes(const armnn::TensorShape& inputShape,
270                                        const armnn::TensorShape& weightsShape,
271                                        const armnn::TensorShape& outputShape,
272                                        bool  transposeWeightMatrix)
273 {
274     unsigned int dimIdx = transposeWeightMatrix ? 0 : 1;
275     return (inputShape[0] == outputShape[0] && weightsShape[dimIdx] == outputShape[1]);
276 }
277 
BroadcastTensor(LayerInputHandle & input0,LayerInputHandle & input1,armnn::IConnectableLayer * startLayer,ConversionData & data)278 bool BroadcastTensor(LayerInputHandle& input0,
279                      LayerInputHandle& input1,
280                      armnn::IConnectableLayer* startLayer,
281                      ConversionData& data)
282 {
283     ARMNN_ASSERT(startLayer != nullptr);
284 
285     const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
286     const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
287 
288     unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
289     unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
290 
291     if (inputDimensions0 == inputDimensions1)
292     {
293         // The inputs have the same number of dimensions, simply connect them to the given layer as they are
294         input0.Connect(startLayer->GetInputSlot(0));
295         input1.Connect(startLayer->GetInputSlot(1));
296 
297         return true;
298     }
299 
300     // Since the number of dimensions do not match then we need to add degenerate dimensions
301     // to the "smaller" tensor using a reshape, while keeping the order of the inputs.
302 
303     unsigned int maxInputDimensions = std::max(inputDimensions0, inputDimensions1);
304     unsigned int sizeDifference = std::abs(armnn::numeric_cast<int>(inputDimensions0) -
305                                            armnn::numeric_cast<int>(inputDimensions1));
306 
307     bool input0IsSmaller = inputDimensions0 < inputDimensions1;
308     LayerInputHandle& smallInputHandle = input0IsSmaller ? input0 : input1;
309     const armnn::TensorInfo& smallInfo = smallInputHandle.GetTensorInfo();
310 
311     const armnn::TensorShape& smallShape = smallInfo.GetShape();
312     std::vector<unsigned int> reshapedDimensions(maxInputDimensions, 1);
313     for (unsigned int i = sizeDifference; i < maxInputDimensions; i++)
314     {
315         reshapedDimensions[i] = smallShape[i - sizeDifference];
316     }
317 
318     armnn::TensorInfo reshapedInfo = smallInfo;
319     reshapedInfo.SetShape(armnn::TensorShape{ armnn::numeric_cast<unsigned int>(reshapedDimensions.size()),
320                                               reshapedDimensions.data() });
321 
322     // RehsapeDescriptor that is ignored in the IsReshapeSupported function
323     armnn::ReshapeDescriptor reshapeDescriptor;
324 
325     bool isSupported = false;
326     armnn::BackendId setBackend;
327     FORWARD_LAYER_SUPPORT_FUNC(__func__,
328                                IsReshapeSupported,
329                                data.m_Backends,
330                                isSupported,
331                                setBackend,
332                                smallInfo,
333                                reshapedInfo,
334                                reshapeDescriptor);
335     if (!isSupported)
336     {
337         return false;
338     }
339 
340     ARMNN_ASSERT(data.m_Network != nullptr);
341     armnn::IConnectableLayer& reshapeLayer = AddReshapeLayer(*data.m_Network, smallInputHandle, reshapedInfo);
342     reshapeLayer.SetBackendId(setBackend);
343 
344     if (input0IsSmaller)
345     {
346         // Input0 is the "smaller" tensor, connect the reshape layer as follows:
347         //
348         //  Input0 Input1
349         //     |     |
350         //  Reshape  |
351         //      \   /
352         //    StartLayer
353 
354         reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
355         input1.Connect(startLayer->GetInputSlot(1));
356     }
357     else
358     {
359         // Input1 is the "smaller" tensor, connect the reshape layer as follows:
360         //
361         //  Input0 Input1
362         //     |     |
363         //     |  Reshape
364         //      \   /
365         //    StartLayer
366 
367         input0.Connect(startLayer->GetInputSlot(0));
368         reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(1));
369     }
370 
371     return true;
372 }
373 
CalcPadding(uint32_t input,uint32_t kernel,uint32_t stride,uint32_t & outPadHead,uint32_t & outPadTail,PaddingScheme scheme)374 void CalcPadding(uint32_t input,
375                  uint32_t kernel,
376                  uint32_t stride,
377                  uint32_t& outPadHead,
378                  uint32_t& outPadTail,
379                  PaddingScheme scheme)
380 {
381     int32_t padHead;
382     int32_t padTail;
383     calculateExplicitPadding(input, stride, kernel, scheme, &padHead, &padTail);
384     outPadHead = armnn::numeric_cast<uint32_t>(padHead);
385     outPadTail = armnn::numeric_cast<uint32_t>(padTail);
386 }
387 
CalcPadding(uint32_t input,uint32_t kernel,uint32_t stride,uint32_t dilation,uint32_t & outPadHead,uint32_t & outPadTail,::android::nn::PaddingScheme scheme)388 void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t dilation, uint32_t& outPadHead,
389                  uint32_t& outPadTail, ::android::nn::PaddingScheme scheme)
390 {
391     int32_t padHead;
392     int32_t padTail;
393     calculateExplicitPadding(input, stride, dilation, kernel, scheme, &padHead, &padTail);
394     outPadHead = armnn::numeric_cast<uint32_t>(padHead);
395     outPadTail = armnn::numeric_cast<uint32_t>(padTail);
396 }
397 
CalcPaddingTransposeConv(uint32_t output,uint32_t kernel,int32_t stride,int32_t & outPadHead,int32_t & outPadTail,::android::nn::PaddingScheme scheme)398 inline void CalcPaddingTransposeConv(uint32_t output, uint32_t kernel, int32_t stride, int32_t& outPadHead,
399                               int32_t& outPadTail, ::android::nn::PaddingScheme scheme)
400 {
401     calculateExplicitPaddingTransposeConv(output, stride, kernel, scheme, &outPadHead, &outPadTail);
402 }
403 
GetOperandShape(const Operand & operand)404 Shape GetOperandShape(const Operand& operand)
405 {
406     Shape shape;
407     shape.type = OperandType(operand.type);
408     shape.dimensions = operand.dimensions;
409     shape.scale = operand.scale;
410     shape.offset = operand.zeroPoint;
411     return shape;
412 }
413 
414 
415 // ArmNN requires the bias scale to be equal to the product of the weight and input scales, which is also
416 // what AndroidNN requires. However for some of the AndroidNN tests the values don't exactly match so
417 // we accept some tolerance. We don't want ArmNN itself to accept these inconsistencies as it is up to the
418 // user (us, in this case) to ensure they match.
SanitizeBiasQuantizationScale(armnn::TensorInfo & biasInfo,const armnn::TensorInfo & weightInfo,const armnn::TensorInfo & inputInfo)419 void SanitizeBiasQuantizationScale(armnn::TensorInfo& biasInfo,
420                                    const armnn::TensorInfo& weightInfo,
421                                    const armnn::TensorInfo& inputInfo)
422 {
423     if (weightInfo.HasPerAxisQuantization())
424     {
425         // NOTE: Bias scale is always set to 0 for per-axis quantization and
426         // it needs to be calculated: scale[i] = input_scale * weight_scale[i]
427         auto UpdateBiasScaleValue = [&inputInfo](float biasScale) -> float
428         {
429             return biasScale * inputInfo.GetQuantizationScale();
430         };
431 
432         std::vector<float> biasScales(weightInfo.GetQuantizationScales());
433         std::transform(biasScales.begin(), biasScales.end(), biasScales.begin(), UpdateBiasScaleValue);
434 
435         biasInfo.SetQuantizationScales(biasScales);
436         // bias is expected to be a 1d tensor, set qdim=0
437         biasInfo.SetQuantizationDim(0);
438 
439         VLOG(DRIVER) << "Bias quantization params have been updated for per-axis quantization";
440     }
441     else
442     {
443         const float expectedBiasScale = weightInfo.GetQuantizationScale() * inputInfo.GetQuantizationScale();
444         if (biasInfo.GetQuantizationScale() != expectedBiasScale)
445         {
446             if (armnnUtils::within_percentage_tolerance(biasInfo.GetQuantizationScale(), expectedBiasScale, 1.0f))
447             {
448                 VLOG(DRIVER) << "Bias quantization scale has been modified to match input * weights";
449                 biasInfo.SetQuantizationScale(expectedBiasScale);
450             }
451         }
452     }
453 }
454 
455 // 4D Tensor Permutations
456 const armnn::PermutationVector IdentityPermutation4D({ 0U, 1U, 2U, 3U });
457 const armnn::PermutationVector IdentityPermutation3D({ 0U, 1U, 2U });
458 const armnn::PermutationVector SwapDim2And3({ 0U, 1U, 3U, 2U });
459 
460 // 3D Permutation Vectors
461 const armnn::PermutationVector RotateTensorLeft({ 1U, 2U, 0U });
462 const armnn::PermutationVector RotateTensorRight({ 2U, 0U, 1U });
463 
464 template<typename OSlot>
AddTransposeLayer(armnn::INetwork & network,OSlot & input,const armnn::PermutationVector & mappings)465 armnn::IConnectableLayer& AddTransposeLayer(armnn::INetwork& network, OSlot& input,
466                                             const armnn::PermutationVector& mappings)
467 {
468     // Add swizzle layer
469     armnn::IConnectableLayer* const layer = network.AddTransposeLayer(mappings);
470 
471     ARMNN_ASSERT(layer != nullptr);
472 
473     // Connect input to swizzle layer
474     input.Connect(layer->GetInputSlot(0));
475 
476     // Setup swizzled output
477     const armnn::TensorInfo outInfo = armnnUtils::TransposeTensorShape(input.GetTensorInfo(), mappings);
478     layer->GetOutputSlot(0).SetTensorInfo(outInfo);
479 
480     return *layer;
481 }
482 
ValidateConcatOutputShape(const std::vector<armnn::TensorShape> & inputShapes,const armnn::TensorShape & outputShape,uint32_t concatDim)483 bool ValidateConcatOutputShape(const std::vector<armnn::TensorShape> & inputShapes,
484                                const armnn::TensorShape & outputShape,
485                                uint32_t concatDim)
486 {
487     // Validate the output shape is correct given the input shapes (which have just been validated)
488     unsigned int numDimensions = inputShapes[0].GetNumDimensions();
489     if (outputShape.GetNumDimensions() != numDimensions)
490     {
491         return Fail("%s: Output shape has wrong number of dimensions", __func__);
492     }
493 
494     unsigned int outputSizeAlongConcatenatedDimension = 0;
495     for (unsigned int i = 0; i < inputShapes.size(); i++)
496     {
497         outputSizeAlongConcatenatedDimension += inputShapes[i][concatDim];
498     }
499 
500     for (unsigned int i = 0; i < numDimensions; ++i)
501     {
502         if (i == concatDim)
503         {
504             if (outputShape[i] != outputSizeAlongConcatenatedDimension)
505             {
506                 return Fail(
507                         "%s: Invalid output shape for dimension %d (%d != %d)",
508                         __func__,
509                         i,
510                         outputShape[i],
511                         outputSizeAlongConcatenatedDimension);
512             }
513         }
514         else
515         {
516             if (outputShape[i] != inputShapes[0][i])
517             {
518                 return Fail("%s: Invalid output shape", __func__);
519             }
520         }
521     }
522 
523     return true;
524 }
525 
RequiresReshape(armnn::TensorShape & inputShape)526 inline bool RequiresReshape(armnn::TensorShape & inputShape)
527 {
528     return inputShape.GetNumDimensions() < 3;
529 }
530 
SwizzleInputs(armnn::INetwork & network,std::vector<LayerInputHandle> & inputs,std::vector<armnn::TensorShape> & inputShapes,const armnn::PermutationVector & mapping,std::vector<armnn::BackendId> & setBackends)531 inline void SwizzleInputs(armnn::INetwork& network,
532                    std::vector<LayerInputHandle>& inputs,
533                    std::vector<armnn::TensorShape>& inputShapes,
534                    const armnn::PermutationVector& mapping,
535                    std::vector<armnn::BackendId>& setBackends)
536 {
537     if (!mapping.IsEqual(IdentityPermutation4D))
538     {
539         size_t nInputs = inputs.size();
540         for (size_t i=0; i<nInputs; ++i)
541         {
542             // add swizzle layer
543             armnn::IConnectableLayer& swizzleLayer = AddTransposeLayer(network, inputs[i], mapping);
544             swizzleLayer.SetBackendId(setBackends[i]);
545             auto& outputSlot = swizzleLayer.GetOutputSlot(0);
546             auto& outputInfo = outputSlot.GetTensorInfo();
547             // replace inputs with the swizzled ones
548             inputs[i] = LayerInputHandle(true, &outputSlot, outputInfo);
549             inputShapes[i] = inputs[i].GetTensorInfo().GetShape();
550         }
551     }
552 }
553 
TransposeInputTensors(ConversionData & data,std::vector<LayerInputHandle> & inputs,std::vector<armnn::TensorShape> & inputShapes,const armnn::PermutationVector & mapping)554 bool TransposeInputTensors(ConversionData& data,
555                           std::vector<LayerInputHandle>& inputs,
556                           std::vector<armnn::TensorShape>& inputShapes,
557                           const armnn::PermutationVector& mapping)
558 {
559     // If we have a IdentityPermutation4D or IdentityPermutation3D then we are not permuting
560     if (!mapping.IsEqual(IdentityPermutation4D) && !mapping.IsEqual(IdentityPermutation3D))
561     {
562         std::vector<armnn::BackendId> setBackendsVec;
563         armnn::TensorInfo outputTransposeInfo;
564         size_t nInputs = inputs.size();
565         for (size_t i=0; i<nInputs; ++i)
566         {
567             // check permute layer
568             armnn::TransposeDescriptor transposeDesc;
569             transposeDesc.m_DimMappings = mapping;
570             outputTransposeInfo = armnnUtils::TransposeTensorShape(inputs[i].GetTensorInfo(), mapping);
571 
572             bool isSupported = false;
573             armnn::BackendId setBackend;
574             FORWARD_LAYER_SUPPORT_FUNC(__func__,
575                                        IsTransposeSupported,
576                                        data.m_Backends,
577                                        isSupported,
578                                        setBackend,
579                                        inputs[i].GetTensorInfo(),
580                                        outputTransposeInfo,
581                                        transposeDesc);
582             setBackendsVec.push_back(setBackend);
583             if (!isSupported)
584             {
585                 return false;
586             }
587 
588         }
589         SwizzleInputs(*data.m_Network, inputs, inputShapes, mapping, setBackendsVec);
590     }
591     return true;
592 }
593 
CreateConcatPermutationParameters(const unsigned int numberOfDimensions,int32_t & concatDimension,std::pair<armnn::PermutationVector,armnn::PermutationVector> & permutationPair)594 bool CreateConcatPermutationParameters(const unsigned int numberOfDimensions,
595                                        int32_t & concatDimension,
596                                        std::pair<armnn::PermutationVector, armnn::PermutationVector> & permutationPair)
597 {
598     bool needPermute = false;
599     ARMNN_ASSERT(numberOfDimensions >= 3);
600 
601     // ArmNN uses Compute Library subtensors to perform concatenation
602     // This only works when concatenating along dimension 0, 1 or 3 for a 4-D tensor,
603     // or along dimension 0 or 2 for a 3-D tensor.
604     if (numberOfDimensions == 4 && concatDimension == 2)
605     {
606         concatDimension = 3;
607         permutationPair = std::make_pair(SwapDim2And3, SwapDim2And3);
608         needPermute = true;
609     }
610     else if (numberOfDimensions == 3 && concatDimension == 1)
611     {
612         concatDimension = 0;
613         permutationPair = std::make_pair(RotateTensorLeft, RotateTensorRight);
614         needPermute = true;
615     }
616     // If the tensor is 3-D and the concat dimension is 2 then we don't need to permute but we do need to change the
617     // permutation identity to only have 3 dimensions
618     else if (numberOfDimensions == 3 && concatDimension == 2)
619     {
620         permutationPair = std::make_pair(IdentityPermutation3D, IdentityPermutation3D);
621     }
622     return needPermute;
623 }
624 
625 } // anonymous namespace
626 
627 namespace armnn_driver
628 {
629 using namespace android::nn;
630 
631 //// Creates an ArmNN activation layer and connects it to the given layer, if the
632 //// passed in AndroidNN activation function requires so.
633 //// @return The end layer of the sequence of layers built for the given AndroidNN
634 //// activation function or nullptr if an error occurred (e.g. unsupported activation).
635 //// Note that the end layer matches the input layer if no activation is required
636 //// (the sequence of layers has length 1).
637 armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
638                                             ActivationFn activation,
639                                             armnn::IConnectableLayer* prevLayer,
640                                             ConversionData& data);
641 
642 
GetInputOperand(const Operation & operation,uint32_t inputIndex,const Model & model,bool failOnIndexOutOfBounds=true)643 inline const Operand* GetInputOperand(const Operation& operation,
644                                       uint32_t inputIndex,
645                                       const Model& model,
646                                       bool failOnIndexOutOfBounds = true)
647 {
648     if (inputIndex >= operation.inputs.size())
649     {
650         if (failOnIndexOutOfBounds)
651         {
652             Fail("%s: invalid input index: %i out of %i", __func__, inputIndex, operation.inputs.size());
653         }
654         return nullptr;
655     }
656 
657     // Model should have been validated beforehand
658     ARMNN_ASSERT(operation.inputs[inputIndex] < getMainModel(model).operands.size());
659     return &getMainModel(model).operands[operation.inputs[inputIndex]];
660 }
661 
GetOutputOperand(const Operation & operation,uint32_t outputIndex,const Model & model)662 inline const Operand* GetOutputOperand(const Operation& operation,
663                                        uint32_t outputIndex,
664                                        const Model& model)
665 {
666     if (outputIndex >= operation.outputs.size())
667     {
668         Fail("%s: invalid output index: %i out of %i", __func__, outputIndex, operation.outputs.size());
669         return nullptr;
670     }
671 
672     // Model should have been validated beforehand
673     ARMNN_ASSERT(operation.outputs[outputIndex] < getMainModel(model).operands.size());
674 
675     return &getMainModel(model).operands[operation.outputs[outputIndex]];
676 }
677 
678 const void* GetOperandValueReadOnlyAddress(const Operand& operand,
679                                            const Model& model,
680                                            const ConversionData& data,
681                                            bool optional = false);
682 
GetOperandType(const Operation & operation,uint32_t inputIndex,const Model & model,OperandType & type)683 inline bool GetOperandType(const Operation& operation,
684                            uint32_t inputIndex,
685                            const Model& model,
686                            OperandType& type)
687 {
688     const Operand* operand = GetInputOperand(operation, inputIndex, model);
689     if (!operand)
690     {
691         return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
692     }
693 
694     type = operand->type;
695     return true;
696 }
697 
IsOperandConstant(const Operand & operand)698 inline bool IsOperandConstant(const Operand& operand)
699 {
700     OperandLifeTime lifetime = operand.lifetime;
701 
702     return lifetime == OperandLifeTime::CONSTANT_COPY ||
703            lifetime == OperandLifeTime::CONSTANT_REFERENCE ||
704            lifetime == OperandLifeTime::POINTER ||
705            lifetime == OperandLifeTime::NO_VALUE;
706 }
707 
708 bool IsWeightsValid(const Operation& operation, uint32_t inputIndex, const Model& model);
709 
710 ConstTensorPin ConvertOperandToConstTensorPin(const Operand& operand,
711                                               const Model& model,
712                                               const ConversionData& data,
713                                               const armnn::PermutationVector& dimensionMappings = g_DontPermute,
714                                               const armnn::TensorShape* overrideTensorShape = nullptr,
715                                               bool optional = false,
716                                               const armnn::DataType* overrideDataType = nullptr);
717 
ConvertOperationInputToConstTensorPin(const Operation & operation,uint32_t inputIndex,const Model & model,const ConversionData & data,const armnn::PermutationVector & dimensionMappings=g_DontPermute,const armnn::TensorShape * overrideTensorShape=nullptr,bool optional=false)718 inline ConstTensorPin ConvertOperationInputToConstTensorPin(
719         const Operation& operation,
720         uint32_t inputIndex,
721         const Model& model,
722         const ConversionData& data,
723         const armnn::PermutationVector& dimensionMappings = g_DontPermute,
724         const armnn::TensorShape* overrideTensorShape = nullptr,
725         bool optional = false)
726 {
727     const Operand* operand = GetInputOperand(operation, inputIndex, model);
728     if (!operand)
729     {
730         Fail("%s: failed to get input operand: index=%u", __func__, inputIndex);
731         return ConstTensorPin();
732     }
733     return ConvertOperandToConstTensorPin(*operand,
734                                           model,
735                                           data,
736                                           dimensionMappings,
737                                           overrideTensorShape,
738                                           optional);
739 }
740 
741 template <typename OutputType>
GetInputScalar(const Operation & operation,uint32_t inputIndex,OperandType type,OutputType & outValue,const Model & model,const ConversionData & data,bool optional=false)742 bool GetInputScalar(const Operation& operation,
743                     uint32_t inputIndex,
744                     OperandType type,
745                     OutputType& outValue,
746                     const Model& model,
747                     const ConversionData& data,
748                     bool optional = false)
749 {
750     const Operand* operand = GetInputOperand(operation, inputIndex, model);
751     if (!optional && !operand)
752     {
753         return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
754     }
755 
756     if (!optional && operand->type != type)
757     {
758         VLOG(DRIVER) << __func__ << ": unexpected operand type: " << operand->type << " should be: " << type;
759         return false;
760     }
761 
762     if (!optional && operand->location.length != sizeof(OutputType))
763     {
764         return Fail("%s: incorrect operand location length: %i (should be %i)",
765                     __func__, operand->location.length, sizeof(OutputType));
766     }
767 
768     const void* valueAddress = GetOperandValueReadOnlyAddress(*operand, model, data);
769     if (!optional && !valueAddress)
770     {
771         return Fail("%s: failed to get address for operand", __func__);
772     }
773 
774     if(!optional)
775     {
776         outValue = *(static_cast<const OutputType*>(valueAddress));
777     }
778 
779     return true;
780 }
781 
GetInputInt32(const Operation & operation,uint32_t inputIndex,int32_t & outValue,const Model & model,const ConversionData & data)782 inline bool GetInputInt32(const Operation& operation,
783                           uint32_t inputIndex,
784                           int32_t& outValue,
785                           const Model& model,
786                           const ConversionData& data)
787 {
788     return GetInputScalar(operation, inputIndex, OperandType::INT32, outValue, model, data);
789 }
790 
GetInputFloat32(const Operation & operation,uint32_t inputIndex,float & outValue,const Model & model,const ConversionData & data)791 inline bool GetInputFloat32(const Operation& operation,
792                             uint32_t inputIndex,
793                             float& outValue,
794                             const Model& model,
795                             const ConversionData& data)
796 {
797     return GetInputScalar(operation, inputIndex, OperandType::FLOAT32, outValue, model, data);
798 }
799 
GetInputActivationFunctionImpl(const Operation & operation,uint32_t inputIndex,OperandType type,ActivationFn & outActivationFunction,const Model & model,const ConversionData & data)800 inline bool GetInputActivationFunctionImpl(const Operation& operation,
801                                            uint32_t inputIndex,
802                                            OperandType type,
803                                            ActivationFn& outActivationFunction,
804                                            const Model& model,
805                                            const ConversionData& data)
806 {
807     if (type != OperandType::INT32 && type != OperandType::TENSOR_INT32)
808     {
809         VLOG(DRIVER) << __func__ << ": unexpected operand type: " << type
810                      << " should be OperandType::INT32 or OperandType::TENSOR_INT32";
811         return false;
812     }
813 
814     int32_t activationFunctionAsInt;
815     if (!GetInputScalar(operation, inputIndex, type, activationFunctionAsInt, model, data))
816     {
817         return Fail("%s: failed to get activation input value", __func__);
818     }
819     outActivationFunction = static_cast<ActivationFn>(activationFunctionAsInt);
820     return true;
821 }
822 
GetInputActivationFunction(const Operation & operation,uint32_t inputIndex,ActivationFn & outActivationFunction,const Model & model,const ConversionData & data)823 inline bool GetInputActivationFunction(const Operation& operation,
824                                        uint32_t inputIndex,
825                                        ActivationFn& outActivationFunction,
826                                        const Model& model,
827                                        const ConversionData& data)
828 {
829     return GetInputActivationFunctionImpl(operation,
830                                           inputIndex,
831                                           OperandType::INT32,
832                                           outActivationFunction,
833                                           model,
834                                           data);
835 }
836 
GetInputActivationFunctionFromTensor(const Operation & operation,uint32_t inputIndex,ActivationFn & outActivationFunction,const Model & model,const ConversionData & data)837 inline bool GetInputActivationFunctionFromTensor(const Operation& operation,
838                                                  uint32_t inputIndex,
839                                                  ActivationFn& outActivationFunction,
840                                                  const Model& model,
841                                                  const ConversionData& data)
842 {
843     // This only accepts a 1-D tensor of size 1
844     return GetInputActivationFunctionImpl(operation,
845                                           inputIndex,
846                                           OperandType::INT32,
847                                           outActivationFunction,
848                                           model,
849                                           data);
850 }
851 
852 
GetOptionalInputActivation(const Operation & operation,uint32_t inputIndex,ActivationFn & activationFunction,const Model & model,const ConversionData & data)853 inline bool GetOptionalInputActivation(const Operation& operation,
854                                        uint32_t inputIndex,
855                                        ActivationFn& activationFunction,
856                                        const Model& model,
857                                        const ConversionData& data)
858 {
859     if (operation.inputs.size() <= inputIndex)
860     {
861         activationFunction = ActivationFn::kActivationNone;
862     }
863     else
864     {
865         if (!GetInputActivationFunction(operation, inputIndex, activationFunction, model, data))
866         {
867             return Fail("%s: Operation has invalid inputs", __func__);
868         }
869     }
870     return true;
871 }
872 
873 template<typename ConvolutionDescriptor>
GetOptionalConvolutionDilationParams(const Operation & operation,uint32_t dilationXIndex,ConvolutionDescriptor & descriptor,const Model & model,const ConversionData & data)874 bool GetOptionalConvolutionDilationParams(const Operation& operation,
875                                           uint32_t dilationXIndex,
876                                           ConvolutionDescriptor& descriptor,
877                                           const Model& model,
878                                           const ConversionData& data)
879 {
880     bool success = true;
881     if (operation.inputs.size() >= dilationXIndex + 2)
882     {
883         success &= GetInputScalar(operation,
884                                   dilationXIndex,
885                                   OperandType::INT32,
886                                   descriptor.m_DilationX,
887                                   model,
888                                   data);
889         success &= GetInputScalar(operation,
890                                   dilationXIndex + 1,
891                                   OperandType::INT32,
892                                   descriptor.m_DilationY,
893                                   model,
894                                   data);
895     }
896 
897     return success;
898 }
899 
GetOptionalBool(const Operation & operation,uint32_t inputIndex,const Model & model,const ConversionData & data)900 inline bool GetOptionalBool(const Operation& operation,
901                             uint32_t inputIndex,
902                             const Model& model,
903                             const ConversionData& data)
904 {
905     const Operand* operand = GetInputOperand(operation, inputIndex, model);
906     if (!operand)
907     {
908         return false;
909     }
910 
911     if (!IsBool(*operand))
912     {
913         return false;
914     }
915 
916     const void* valueAddress = GetOperandValueReadOnlyAddress(*operand, model, data);
917     if (!valueAddress)
918     {
919         return false;
920     }
921 
922     return *(static_cast<const bool*>(valueAddress));
923 }
924 
925 bool GetTensorInt32Values(const Operand& operand,
926                                  std::vector<int32_t>& outValues,
927                                  const Model& model,
928                                  const ConversionData& data);
929 
930 bool GetInputPaddingScheme(const Operation& operation,
931                            uint32_t inputIndex,
932                            PaddingScheme& outPaddingScheme,
933                            const Model& model,
934                            const ConversionData& data);
935 
936 LayerInputHandle ConvertToLayerInputHandle(const Operation& operation,
937                                            uint32_t inputIndex,
938                                            const Model& model,
939                                            ConversionData& data,
940                                            const armnn::PermutationVector& dimensionMappings = g_DontPermute,
941                                            const LayerInputHandle* inputHandle = nullptr);
942 
943 bool SetupAndTrackLayerOutputSlot(const Operation& operation,
944                                   uint32_t operationOutputIndex,
945                                   armnn::IConnectableLayer& layer,
946                                   uint32_t layerOutputIndex,
947                                   const Model& model,
948                                   ConversionData& data,
949                                   const armnn::TensorInfo* overrideOutputInfo = nullptr,
950                                   const std::function <void (const armnn::TensorInfo&, bool&)>& validateFunc = nullptr,
951                                   const ActivationFn& activationFunction = ActivationFn::kActivationNone,
952                                   bool inferOutputShapes = false);
953 
954 armnn::DataLayout OptionalDataLayout(const Operation& operation,
955                                      uint32_t inputIndex,
956                                      const Model& model,
957                                      ConversionData& data);
958 
SetupAndTrackLayerOutputSlot(const Operation & operation,uint32_t outputIndex,armnn::IConnectableLayer & layer,const Model & model,ConversionData & data,const armnn::TensorInfo * overrideOutputInfo=nullptr,const std::function<void (const armnn::TensorInfo &,bool &)> & validateFunc=nullptr,const ActivationFn & activationFunction=ActivationFn::kActivationNone)959 inline bool SetupAndTrackLayerOutputSlot(
960         const Operation& operation,
961         uint32_t outputIndex,
962         armnn::IConnectableLayer& layer,
963         const Model& model,
964         ConversionData& data,
965         const armnn::TensorInfo* overrideOutputInfo = nullptr,
966         const std::function <void (const armnn::TensorInfo&, bool&)>& validateFunc = nullptr,
967         const ActivationFn& activationFunction = ActivationFn::kActivationNone)
968 {
969     return SetupAndTrackLayerOutputSlot(operation,
970                                         outputIndex,
971                                         layer,
972                                         outputIndex,
973                                         model,
974                                         data,
975                                         overrideOutputInfo,
976                                         validateFunc,
977                                         activationFunction);
978 }
979 
980 bool ConvertToActivation(const Operation& operation,
981                          const char* operationName,
982                          const armnn::ActivationDescriptor& activationDesc,
983                          const Model& model,
984                          ConversionData& data);
985 
986 bool ConvertPaddings(const Operation& operation,
987                      const Model& model,
988                      ConversionData& data,
989                      unsigned int rank,
990                      armnn::PadDescriptor& padDescriptor);
991 bool ConvertReduce(const Operation& operation,
992                    const Model& model,
993                    ConversionData& data,
994                    armnn::ReduceOperation reduceOperation);
995 
996 bool ConvertPooling2d(const Operation& operation,
997                       const char* operationName,
998                       armnn::PoolingAlgorithm poolType,
999                       const Model& model,
1000                       ConversionData& data);
1001 
IsQSymm8(const Operand & operand)1002 inline bool IsQSymm8(const Operand& operand)
1003 {
1004     return operand.type == OperandType::TENSOR_QUANT8_SYMM;
1005 }
1006 
1007 enum class DequantizeStatus
1008 {
1009     SUCCESS,
1010     NOT_REQUIRED,
1011     INVALID_OPERAND
1012 };
1013 
1014 using DequantizeResult = std::tuple<std::unique_ptr<float[]>, size_t, armnn::TensorInfo, DequantizeStatus>;
1015 
1016 DequantizeResult DequantizeIfRequired(size_t operand_index,
1017                                       const Operation& operation,
1018                                       const Model& model,
1019                                       const ConversionData& data);
1020 
1021 ConstTensorPin DequantizeAndMakeConstTensorPin(const Operation& operation,
1022                                                const Model& model,
1023                                                const ConversionData& data,
1024                                                size_t operandIndex,
1025                                                bool optional = false);
1026 
1027 bool IsConnectedToDequantize(armnn::IOutputSlot* ioutputSlot);
1028 
1029 } // namespace armnn_driver
1030