1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "conv2d_builder.h"
17 
18 #include "transform.h"
19 #include "validation.h"
20 #include "ops_validation.h"
21 
22 namespace OHOS {
23 namespace NeuralNetworkRuntime {
24 namespace Ops {
25 static constexpr int INPUT_NUM = 3;
26 static constexpr int OUTPUT_NUM = 1;
27 static constexpr int PARAM_MAX_NUM = 6;
28 static constexpr int CONV2D_INPUT_WEIGHT = 1;
29 static constexpr int WEIGHT_SIZE = 4;
30 static constexpr int OUT_CHANNEL_INDEX = 0;
31 static constexpr int IN_CHANNEL_INDEX = 3;
32 static constexpr int KERNEL_HEIGHT_INDEX = 1;
33 static constexpr int KERNEL_WEIGHT_INDEX = 2;
34 static constexpr int PAD_MODE_GET = 1;
35 static constexpr int PAD_LIST_GET = 4;
36 static constexpr int SCALAR_LENGTH = 1;
37 static const std::string OP_NAME = "Conv2D";
38 
Conv2DBuilder()39 Conv2DBuilder::Conv2DBuilder() {}
40 
~Conv2DBuilder()41 Conv2DBuilder::~Conv2DBuilder() {}
42 
SetInputAndOutput(const std::vector<uint32_t> & inputsIndex,const std::vector<uint32_t> & outputsIndex,const std::vector<std::shared_ptr<NNTensor>> & allTensors)43 OH_NN_ReturnCode Conv2DBuilder::SetInputAndOutput(const std::vector<uint32_t>& inputsIndex,
44                                                   const std::vector<uint32_t>& outputsIndex,
45                                                   const std::vector<std::shared_ptr<NNTensor>>& allTensors)
46 {
47     OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
48     if (returnCode != OH_NN_SUCCESS) {
49         LOGE("[Conv2d] SetInputAndOutput failed, passed invalid input or output index.");
50         return returnCode;
51     }
52 
53     m_inputsIndex = inputsIndex;
54     m_outputsIndex = outputsIndex;
55 
56     return OH_NN_SUCCESS;
57 }
58 
SetChannel(const std::vector<uint32_t> & inputsIndex,const std::vector<std::shared_ptr<NNTensor>> & allTensors)59 OH_NN_ReturnCode Conv2DBuilder::SetChannel(const std::vector<uint32_t>& inputsIndex,
60                                            const std::vector<std::shared_ptr<NNTensor>>& allTensors)
61 {
62     // set inChannel, outChannel, kernelSize
63     auto weightShape = allTensors[inputsIndex[CONV2D_INPUT_WEIGHT]]->GetDimensions();
64     if (weightShape.size() != WEIGHT_SIZE) {
65         LOGE("[Conv2d] SetChannel failed, the dimension of weight should be %d", WEIGHT_SIZE);
66         return OH_NN_INVALID_PARAMETER;
67     }
68 
69     m_inChannel = weightShape[IN_CHANNEL_INDEX];
70     m_outChannel = weightShape[OUT_CHANNEL_INDEX];
71 
72     return OH_NN_SUCCESS;
73 }
74 
SetKernelSize(const std::vector<uint32_t> & inputsIndex,const std::vector<std::shared_ptr<NNTensor>> & allTensors)75 void Conv2DBuilder::SetKernelSize(const std::vector<uint32_t>& inputsIndex,
76                                   const std::vector<std::shared_ptr<NNTensor>>& allTensors)
77 {
78     // set inChannel, outChannel, kernelSize
79     auto weightShape = allTensors[inputsIndex[CONV2D_INPUT_WEIGHT]]->GetDimensions();
80 
81     m_kernelSize.clear();
82     m_kernelSize.emplace_back(weightShape[KERNEL_HEIGHT_INDEX]);
83     m_kernelSize.emplace_back(weightShape[KERNEL_WEIGHT_INDEX]);
84 }
85 
SetStrides(const std::shared_ptr<NNTensor> & tensor)86 OH_NN_ReturnCode Conv2DBuilder::SetStrides(const std::shared_ptr<NNTensor>& tensor)
87 {
88     tensor->IdentifyOpParameter();
89     // Set Strides
90     if (tensor->GetDataType() != OH_NN_INT64) {
91         LOGE("[Conv2d] SetStrides failed, the Strides should be type OH_NN_INT64.");
92         return OH_NN_INVALID_PARAMETER;
93     }
94 
95     void* buffer = tensor->GetBuffer();
96     if (buffer == nullptr) {
97         LOGE("[Conv2d] SetStrides GetBuffer return nullptr");
98         return OH_NN_INVALID_PARAMETER;
99     }
100     const int64_t* pStrides = reinterpret_cast<const int64_t*>(buffer);
101     uint32_t stridesSize = tensor->GetElementCount();
102     m_strides.assign(pStrides, pStrides + stridesSize);
103 
104     return OH_NN_SUCCESS;
105 }
106 
SetDilation(const std::shared_ptr<NNTensor> & tensor)107 OH_NN_ReturnCode Conv2DBuilder::SetDilation(const std::shared_ptr<NNTensor>& tensor)
108 {
109     tensor->IdentifyOpParameter();
110     // Set Dilation
111     if (tensor->GetDataType() != OH_NN_INT64) {
112         LOGE("[Conv2d] SetDilation failed, the Dilation should have type OH_NN_INT64");
113         return OH_NN_INVALID_PARAMETER;
114     }
115 
116     void* buffer = tensor->GetBuffer();
117     if (buffer == nullptr) {
118         LOGE("[Conv2d] SetDilation GetBuffer return nullptr");
119         return OH_NN_INVALID_PARAMETER;
120     }
121     const int64_t* pDilation = reinterpret_cast<const int64_t*>(buffer);
122     uint32_t dilationSize = tensor->GetElementCount();
123     m_dilation.assign(pDilation, pDilation + dilationSize);
124 
125     return OH_NN_SUCCESS;
126 }
127 
SetPad(const std::shared_ptr<NNTensor> & tensor)128 OH_NN_ReturnCode Conv2DBuilder::SetPad(const std::shared_ptr<NNTensor>& tensor)
129 {
130     tensor->IdentifyOpParameter();
131 
132     bool isPadMode = false;
133     if (tensor->GetElementCount() == PAD_MODE_GET) {
134         isPadMode = true;
135     } else if (tensor->GetElementCount() != PAD_LIST_GET) {
136         LOGE("[Conv2d] SetPad failed, inputs should be 1 for padMode and 4 for padList.");
137         return OH_NN_INVALID_PARAMETER;
138     }
139 
140     void* buffer = tensor->GetBuffer();
141     if (buffer == nullptr) {
142         LOGE("[Conv2d] SetPadList GetBuffer return nullptr");
143         return OH_NN_INVALID_PARAMETER;
144     }
145 
146     // Set PadMode or PadList
147     if (isPadMode) {
148         if (tensor->GetDataType() != OH_NN_INT8) {
149             LOGE("[Conv2d] SetPad failed, the PadMode should have type OH_NN_INT8.");
150             return OH_NN_INVALID_PARAMETER;
151         }
152 
153         int8_t* pPad = static_cast<int8_t*>(buffer);
154         if (!OHOS::NeuralNetworkRuntime::Validation::ValidatePadMode(*pPad)) {
155             LOGE("[Conv2d] SetPad failed, invalid pad mode.");
156             return OH_NN_INVALID_PARAMETER;
157         }
158         m_padMode = NNToMS::TransformPadModeValue(*pPad);
159     } else {
160         if (tensor->GetDataType() != OH_NN_INT64) {
161             LOGE("[Conv2d] SetPad failed, the PadList should have type OH_NN_INT64.");
162             return OH_NN_INVALID_PARAMETER;
163         }
164 
165         int64_t* pPadList = static_cast<int64_t*>(buffer);
166         uint32_t padListSize = tensor->GetElementCount();
167         m_pad.assign(pPadList, pPadList + padListSize);
168     }
169 
170     return OH_NN_SUCCESS;
171 }
172 
SetGroup(const std::shared_ptr<NNTensor> & tensor)173 OH_NN_ReturnCode Conv2DBuilder::SetGroup(const std::shared_ptr<NNTensor>& tensor)
174 {
175     tensor->IdentifyOpParameter();
176     // Set Group
177     if (tensor->GetElementCount() != SCALAR_LENGTH) {
178         LOGE("[Conv2d] SetGroup failed, The Group shoule be a scalar");
179         return OH_NN_INVALID_PARAMETER;
180     }
181 
182     if (tensor->GetDataType() != OH_NN_INT64) {
183         LOGE("[Conv2d] SetGroup failed, The Group should have type OH_NN_INT64.");
184         return OH_NN_INVALID_PARAMETER;
185     }
186 
187     void* buffer = tensor->GetBuffer();
188     if (buffer == nullptr) {
189         LOGE("[Conv2d] SetGroup GetBuffer return nullptr");
190         return OH_NN_INVALID_PARAMETER;
191     }
192     m_group = *static_cast<int64_t*>(buffer);
193 
194     return OH_NN_SUCCESS;
195 }
196 
SetActavitation(const std::shared_ptr<NNTensor> & tensor)197 OH_NN_ReturnCode Conv2DBuilder::SetActavitation(const std::shared_ptr<NNTensor>& tensor)
198 {
199     tensor->IdentifyOpParameter();
200 
201     if (tensor->GetElementCount() != SCALAR_LENGTH) {
202         LOGE("[Conv2d] SetActavitation failed, the ActivationType shoule be a scalar");
203         return OH_NN_INVALID_PARAMETER;
204     }
205 
206     if (tensor->GetDataType() != OH_NN_INT8) {
207         LOGE("[Conv2d] SetActavitation failed, the ActivationType should have type OH_NN_INT8.");
208         return OH_NN_INVALID_PARAMETER;
209     }
210 
211     void* buffer = tensor->GetBuffer();
212     if (buffer == nullptr) {
213         LOGE("[Conv2d] SetGroup GetBuffer return nullptr");
214         return OH_NN_INVALID_PARAMETER;
215     }
216     int8_t* pFuseData = static_cast<int8_t*>(buffer);
217     if (!OHOS::NeuralNetworkRuntime::Validation::ValidateFuseType(static_cast<OH_NN_FuseType>(*pFuseData))) {
218         LOGE("[Conv2d] SetActavitation failed, activation input is invalid.");
219         return OH_NN_INVALID_PARAMETER;
220     }
221     m_activationType = NNToMS::TransfromFusionType((OH_NN_FuseType)(*pFuseData));
222 
223     return OH_NN_SUCCESS;
224 }
225 
Build(const std::vector<uint32_t> & paramsIndex,const std::vector<uint32_t> & inputsIndex,const std::vector<uint32_t> & outputsIndex,const std::vector<std::shared_ptr<NNTensor>> & allTensors)226 OH_NN_ReturnCode Conv2DBuilder::Build(const std::vector<uint32_t>& paramsIndex,
227     const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
228     const std::vector<std::shared_ptr<NNTensor>>& allTensors)
229 {
230     if (m_isBuild) {
231         LOGE("[Conv2d] Build failed, Conv2D operation has been build, cannot build again.");
232         return OH_NN_OPERATION_FORBIDDEN;
233     }
234 
235     OH_NN_ReturnCode returnCode = SetInputAndOutput(inputsIndex, outputsIndex, allTensors);
236     if (returnCode != OH_NN_SUCCESS) {
237         return returnCode;
238     }
239 
240     returnCode = CheckParamIndex(paramsIndex, allTensors, PARAM_MAX_NUM);
241     if (returnCode != OH_NN_SUCCESS) {
242         LOGE("[Conv2d] Build failed, passed invalid invalid index.");
243         return returnCode;
244     }
245 
246     returnCode = SetChannel(inputsIndex, allTensors);
247     if (returnCode != OH_NN_SUCCESS) {
248         return returnCode;
249     }
250 
251     SetKernelSize(inputsIndex, allTensors);
252 
253     for (int i : paramsIndex) {
254         std::shared_ptr<NNTensor> tensor = allTensors[i];
255         if (m_paramMap.find(tensor->GetType()) != m_paramMap.end()) {
256             returnCode = (this->*(m_paramMap[tensor->GetType()]))(tensor);
257         } else {
258             LOGE("[Conv2D] Build failed, param invalid, type=%d", tensor->GetType());
259             return OH_NN_INVALID_PARAMETER;
260         }
261 
262         if (returnCode != OH_NN_SUCCESS) {
263             LOGE("[Conv2D] Build failed, Passed invalid param.");
264             return returnCode;
265         }
266     }
267 
268     // The quantization type of the first output determinies that of the operator.
269     SetQuantType(outputsIndex, allTensors);
270 
271     m_isBuild = true;
272     m_name = OP_NAME;
273     return OH_NN_SUCCESS;
274 }
275 
GetPrimitive()276 LiteGraphPrimitvePtr Conv2DBuilder::GetPrimitive()
277 {
278     if (!m_isBuild) {
279         LOGE("[Conv2d] GetPrimitive failed, Cannot get primitive before call build.");
280         return {nullptr, DestroyLiteGraphPrimitive};
281     }
282 
283     auto primitive = MindIR_Conv2DFusion_CreatePrimitive(m_kernelSize, m_strides,
284         m_dilation, m_padMode, m_pad, m_group, m_inChannel, m_outChannel, m_activationType);
285     LiteGraphPrimitvePtr  graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
286     return graphPrimitivePtr;
287 }
288 
289 REGISTER_OPS(Conv2DBuilder, OH_NN_OPS_CONV2D);
290 } // namespace Ops
291 } // namespace NeuralNetworkRuntime
292 } // namespace OHOS
293