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