xref: /aosp_15_r20/external/armnn/src/backends/tosaCommon/operatorMappings/TransposeConv2dOperator.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1 //
2 // Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "TransposeConv2dOperator.hpp"
7 
8 #include "layers/TransposeConvolution2dLayer.hpp"
9 
ConvertTransposeConv2dToTosaOperator(const Layer * layer,const std::vector<const TensorInfo * > & inputs,const std::vector<const TensorInfo * > & outputs,const TransposeConvolution2dDescriptor * descriptor)10 TosaSerializationBasicBlock* ConvertTransposeConv2dToTosaOperator(const Layer* layer,
11                                                                   const std::vector<const TensorInfo*>& inputs,
12                                                                   const std::vector<const TensorInfo*>& outputs,
13                                                                   const TransposeConvolution2dDescriptor* descriptor)
14 {
15     std::string input0Name = std::string("input0_");
16     std::string input1Name = std::string("constant_") + GetUniqueTosaMappingID();
17     std::string input2Name = std::string("constant_") + GetUniqueTosaMappingID();
18     std::string outputName = std::string("output0_");
19     std::string blockName  = std::string("Op_TRANSPOSE_CONV2D_block_") + GetUniqueTosaMappingID();
20 
21     // If a layer is present then the block will be used for execution, so input and output names need to be determined
22     // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter.
23     if(layer != nullptr)
24     {
25         // Get the layers connected to the input slots and determine unique tensor names.
26         Layer& connectedInputLayer = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer();
27         input0Name = GenerateUniqueName(connectedInputLayer, 0);
28 
29         // Determine unique output tensor name.
30         outputName = GenerateUniqueOutputName(*layer, 0);
31     }
32 
33     std::vector<TosaSerializationTensor*> tensors;
34     std::vector<TosaSerializationOperator*> operators;
35 
36     // Setup input tensor
37     // Only add tensor if connected layer is an input layer.
38     // As intermediate or constant tensors will be created separately.
39     // There also can't be duplicate tensors.
40     if(input0Name.find("input0_") != std::string::npos)
41     {
42         std::vector<int32_t> inputShape0 = GetTosaTensorShape(inputs[0]->GetShape());
43         DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType());
44 
45         tensors.push_back(new TosaSerializationTensor(input0Name, inputShape0, inputDType0, {}));
46     }
47 
48     // Setup weights tensor, constant data will get copied during SetConstantTensorData
49     operators.push_back(new TosaSerializationOperator(Op_CONST, Attribute_NONE, nullptr, {}, {input1Name}));
50 
51     // During validation the TensorInfo can be retrieved from the inputs.
52     // During execution, it is only available through the layer so use m_Weight.
53     if(layer == nullptr)
54     {
55         std::vector<int32_t> inputShape1 = GetTosaTensorShape(inputs[1]->GetShape());
56         DType inputDType1 = ArmNNToDType(inputs[1]->GetDataType());
57 
58         tensors.push_back(new TosaSerializationTensor(input1Name, inputShape1, inputDType1, {}));
59     }
60     else
61     {
62         auto transposeConv2dLayer = PolymorphicDowncast<const TransposeConvolution2dLayer*>(layer);
63 
64         std::vector<int32_t> inputShape1 = GetTosaTensorShape(
65                 transposeConv2dLayer->m_Weight->GetTensorInfo().GetShape());
66         DType inputDType1 = ArmNNToDType(transposeConv2dLayer->m_Weight->GetTensorInfo().GetDataType());
67 
68         std::vector<uint8_t> uint8Data = ConvertConstantTensorDataToBuffer(transposeConv2dLayer->m_Weight);
69         tensors.push_back(new TosaSerializationTensor(input1Name, inputShape1, inputDType1, uint8Data));
70     }
71 
72     // Setup bias operator and tensor, constant data will get copied during SetConstantTensorData
73     operators.push_back(new TosaSerializationOperator(Op_CONST, Attribute_NONE, nullptr, {}, {input2Name}));
74 
75     // During validation the TensorInfo can be retrieved from the inputs.
76     // During execution, it is only available through the layer so use m_Bias.
77     if(layer == nullptr && descriptor->m_BiasEnabled)
78     {
79         std::vector<int32_t> inputShape2 = GetTosaTensorShape(inputs[2]->GetShape());
80         DType inputDType2 = ArmNNToDType(inputs[2]->GetDataType());
81 
82         tensors.push_back(new TosaSerializationTensor(input2Name, inputShape2, inputDType2, {}));
83     }
84     else if(descriptor->m_BiasEnabled)
85     {
86         auto transposeConv2dLayer = PolymorphicDowncast<const TransposeConvolution2dLayer*>(layer);
87 
88         std::vector<int32_t> inputShape2 = GetTosaTensorShape(
89                 transposeConv2dLayer->m_Bias->GetTensorInfo().GetShape());
90         DType inputDType2 = ArmNNToDType(transposeConv2dLayer->m_Bias->GetTensorInfo().GetDataType());
91 
92         std::vector<uint8_t> uint8Data = ConvertConstantTensorDataToBuffer(transposeConv2dLayer->m_Bias);
93         tensors.push_back(new TosaSerializationTensor(input2Name, inputShape2, inputDType2, uint8Data));
94     }
95     else
96     {
97         // If bias is disabled, create a constant bias tensor of 0's as three inputs are required.
98         // The size of the bias must match the channels dimension, so get the correct index.
99         unsigned int index = (descriptor->m_DataLayout == DataLayout::NHWC) ? 3 : 1;
100 
101         std::vector<uint8_t> uint8Data;
102         std::vector<float> data(outputs[0]->GetShape()[index], 0.0f);
103 
104         TosaSerializationHandler::ConvertF32toU8(data, uint8Data);
105 
106         tensors.push_back(new TosaSerializationTensor(input2Name,
107                                                       {static_cast<int32_t>(outputs[0]->GetShape()[index])},
108                                                       DType_FP32,
109                                                       uint8Data));
110     }
111 
112     // Setup Output Tensor
113     std::vector<int32_t> outputShape0 = GetTosaTensorShape(outputs[0]->GetShape());
114     DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType());
115 
116     tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {}));
117 
118     // Set up TRANSPOSE_CONV2D operator
119     // The TOSA Reference Model pads the output shape, so it is added to output shape.
120     // In Arm NN we pad the input shape, so it is taken away.
121     // To offset this the negative padding value can be used.
122     std::vector<int> pad = {-static_cast<int>(descriptor->m_PadTop),
123                             -static_cast<int>(descriptor->m_PadBottom),
124                             -static_cast<int>(descriptor->m_PadLeft),
125                             -static_cast<int>(descriptor->m_PadRight)};
126     std::vector<int> stride = {static_cast<int>(descriptor->m_StrideY),
127                                static_cast<int>(descriptor->m_StrideX)};
128 
129     std::vector<int> outputShape;
130     // If available use shape in descriptor otherwise use output shape.
131     if (descriptor->m_OutputShape.size() == 4)
132     {
133         for (uint32_t i = 0; i < descriptor->m_OutputShape.size(); ++i)
134         {
135             outputShape.push_back(static_cast<int>(descriptor->m_OutputShape[i]));
136         }
137     }
138     else
139     {
140         for (uint32_t i = 0; i < outputs[0]->GetNumDimensions(); ++i)
141         {
142             outputShape.push_back(static_cast<int>(outputs[0]->GetShape()[i]));
143         }
144     }
145 
146     TosaTransposeConvAttribute attribute(pad, stride, outputShape, 0, 0, ArmNNToDType(inputs[0]->GetDataType()));
147 
148     auto* op = new TosaSerializationOperator(Op_TRANSPOSE_CONV2D,
149                                              Attribute_TransposeConvAttribute,
150                                              &attribute,
151                                              {input0Name, input1Name, input2Name},
152                                              {outputName});
153     operators.push_back(op);
154 
155     // operatorInputNames/operatorOutputNames ends up being the same as
156     // blockInputNames/blockOutputNames for one-to-one ArmNN to TOSA mappings
157     return new TosaSerializationBasicBlock(blockName,                            // name
158                                            operators,                            // operators
159                                            tensors,                              // tensors
160                                            {input0Name, input1Name, input2Name}, // inputs
161                                            {outputName});                        // outputs
162 }