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