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 <algorithm>
17 #include <cstdlib>
18 #include <new>
19
20 #include "nn_tensor.h"
21 #include "validation.h"
22 #include "transform.h"
23 #include "common/log.h"
24 #include "mindir.h"
25 #include "mindir_types.h"
26
27 namespace OHOS {
28 namespace NeuralNetworkRuntime {
29 const uint32_t SUPPORT_NUM_BIT = 8; // Currently support 8-bit quantization only
30 const uint32_t INVALID_NUM_BIT = 0;
31
DestroyLiteGraphTensor(void * tensor)32 void DestroyLiteGraphTensor(void* tensor)
33 {
34 mindspore::lite::MindIR_Tensor_Destroy(&tensor);
35 }
36
~NNTensor()37 NNTensor::~NNTensor()
38 {
39 if (m_buffer != nullptr) {
40 delete [] reinterpret_cast<char*>(m_buffer);
41 }
42 }
43
NNTensor(NNTensor && tensor)44 NNTensor::NNTensor(NNTensor&& tensor) noexcept
45 {
46 *this = std::move(tensor);
47 }
48
operator =(NNTensor && tensor)49 NNTensor& NNTensor::operator=(NNTensor&& tensor) noexcept
50 {
51 if (this == &tensor) {
52 return *this;
53 }
54
55 m_type = tensor.m_type;
56 m_dataType = tensor.m_dataType;
57 m_format = tensor.m_format;
58 m_name = std::move(tensor.m_name);
59 m_dimensions = std::move(tensor.m_dimensions);
60 m_quantParams = std::move(tensor.m_quantParams);
61 m_elementCount = tensor.m_elementCount;
62 m_isDynamicShape = tensor.m_isDynamicShape;
63 m_isOpParameter = tensor.m_isOpParameter;
64 m_buffer = tensor.m_buffer;
65 m_bufferLength = tensor.m_bufferLength;
66 m_dataLength = tensor.m_dataLength;
67
68 tensor.m_buffer = nullptr;
69 tensor.m_bufferLength = 0;
70 tensor.m_dataLength = 0;
71
72 return *this;
73 }
74
Build(OH_NN_DataType dataType,const std::vector<int32_t> & dimensions,const std::vector<QuantParam> & quantParam,OH_NN_TensorType type)75 OH_NN_ReturnCode NNTensor::Build(OH_NN_DataType dataType,
76 const std::vector<int32_t>& dimensions,
77 const std::vector<QuantParam>& quantParam,
78 OH_NN_TensorType type)
79 {
80 m_type = type;
81
82 if (!Validation::ValidateTensorDataType(dataType)) {
83 LOGE("Build failed, passed invalid data type.");
84 return OH_NN_INVALID_PARAMETER;
85 }
86 m_dataType = dataType;
87
88 OH_NN_ReturnCode ret = ParseDimensions(dimensions);
89 if (ret != OH_NN_SUCCESS) {
90 LOGE("Build failed, passed invalid dimensions.");
91 return ret;
92 }
93
94 ret = ParseQuantParams(quantParam);
95 if (ret != OH_NN_SUCCESS) {
96 LOGE("Build failed, please check quantParam.");
97 return ret;
98 }
99
100 return OH_NN_SUCCESS;
101 }
102
BuildFromOHNNTensor(const OH_NN_Tensor & nnTensor)103 OH_NN_ReturnCode NNTensor::BuildFromOHNNTensor(const OH_NN_Tensor& nnTensor)
104 {
105 m_type = nnTensor.type;
106
107 if (!Validation::ValidateTensorDataType(nnTensor.dataType)) {
108 LOGE("BuildFromOHNNTensor failed, passed invalid data type: %d.", nnTensor.dataType);
109 return OH_NN_INVALID_PARAMETER;
110 }
111 m_dataType = nnTensor.dataType;
112
113 if (!Validation::ValidateTensorType(nnTensor.type)) {
114 LOGE("BuildFromOHNNTensor failed, passed invalid nnTensor type: %d.", nnTensor.type);
115 return OH_NN_INVALID_PARAMETER;
116 }
117
118 OH_NN_ReturnCode ret = ParseDimensions(nnTensor);
119 if (ret != OH_NN_SUCCESS) {
120 LOGE("BuildFromOHNNTensor failed, passed invalid nnTensor dimensions.");
121 return ret;
122 }
123
124 ret = ParseQuantParams(nnTensor.quantParam);
125 if (ret != OH_NN_SUCCESS) {
126 LOGE("BuildFromOHNNTensor failed, please check quantParam in nnTensor.");
127 return ret;
128 }
129
130 return OH_NN_SUCCESS;
131 }
132
ParseDimensions(const std::vector<int32_t> & dimensions)133 OH_NN_ReturnCode NNTensor::ParseDimensions(const std::vector<int32_t>& dimensions)
134 {
135 // Temporary variable to check overflow.
136 uint64_t absoluteDim {0};
137 uint64_t elementCount {1};
138 uint64_t dataLength {static_cast<uint64_t>(GetTypeSize(m_dataType))};
139 m_isDynamicShape = false;
140 for (int32_t dim : dimensions) {
141 if (dim < -1 || dim == 0) {
142 LOGE("ParseDimension failed, dimension of OH_NN_Tensor cannot be 0 or less than -1, receive %d.", dim);
143 return OH_NN_INVALID_PARAMETER;
144 }
145
146 m_isDynamicShape = m_isDynamicShape || (dim == -1);
147 absoluteDim = static_cast<uint64_t>(abs(dim));
148 elementCount *= absoluteDim;
149 dataLength *= absoluteDim;
150
151 if (dataLength > UINT32_MAX) {
152 LOGE("ParseDimension failed, expected data length of tensor exceed limit %u.", UINT32_MAX);
153 return OH_NN_INVALID_PARAMETER;
154 }
155 }
156
157 if (m_isDynamicShape) {
158 // If tensor has dynamic shape, m_elementCount and m_dataLength take 0.
159 m_elementCount = 0;
160 m_dataLength = 0;
161 } else {
162 m_elementCount = static_cast<uint32_t>(elementCount);
163 m_dataLength = static_cast<size_t>(dataLength);
164 }
165
166 m_dimensions = std::move(dimensions);
167 return OH_NN_SUCCESS;
168 }
169
ParseDimensions(const OH_NN_Tensor & nnTensor)170 OH_NN_ReturnCode NNTensor::ParseDimensions(const OH_NN_Tensor& nnTensor)
171 {
172 OH_NN_ReturnCode ret = Validation::ValidateArray(nnTensor.dimensions, nnTensor.dimensionCount);
173 if (ret != OH_NN_SUCCESS) {
174 LOGE("BuildFromOHNNTensor failed, please check dimension and dimensionCount in NNTensor.");
175 return ret;
176 }
177 std::vector<int32_t> dimensions = ConstructVectorFromArray(nnTensor.dimensions, nnTensor.dimensionCount);
178
179 ret = ParseDimensions(dimensions);
180 if (ret != OH_NN_SUCCESS) {
181 LOGE("BuildFromOHNNTensor failed, passed invalid dimension info.");
182 return ret;
183 }
184
185 return OH_NN_SUCCESS;
186 }
187
ParseQuantParams(const OH_NN_QuantParam * quantParam)188 OH_NN_ReturnCode NNTensor::ParseQuantParams(const OH_NN_QuantParam* quantParam)
189 {
190 if (quantParam == nullptr) {
191 return OH_NN_SUCCESS;
192 }
193
194 if ((quantParam->numBits == nullptr) || (quantParam->scale == nullptr) || (quantParam->zeroPoint == nullptr)) {
195 LOGE("ParseQuantParams failed, scale or zeroPoint is nullptr.");
196 return OH_NN_INVALID_PARAMETER;
197 }
198
199 std::vector<QuantParam> tmpQuantParam;
200 uint32_t numBits{0};
201 double scale{0.0};
202 int32_t zeroPoint{0};
203 for (uint32_t i = 0; i < quantParam->quantCount; i++) {
204 numBits = quantParam->numBits[i];
205 scale = quantParam->scale[i];
206 zeroPoint = quantParam->zeroPoint[i];
207 tmpQuantParam.emplace_back((QuantParam){numBits, scale, zeroPoint});
208 }
209
210 OH_NN_ReturnCode ret = ParseQuantParams(tmpQuantParam);
211 if (ret != OH_NN_SUCCESS) {
212 LOGE("ParseQuantParams failed, please numBits in NNTensor.");
213 return ret;
214 }
215
216 return OH_NN_SUCCESS;
217 }
218
ParseQuantParams(const std::vector<QuantParam> & quantParams)219 OH_NN_ReturnCode NNTensor::ParseQuantParams(const std::vector<QuantParam>& quantParams)
220 {
221 for (const QuantParam& param : quantParams) {
222 // Only support 8-bit quantization in NNR version 1.0
223 if ((param.numBits != SUPPORT_NUM_BIT) || (param.numBits == INVALID_NUM_BIT)) {
224 LOGE("ParseQuantParams failed, get invalid numBits %d.", param.numBits);
225 return OH_NN_INVALID_PARAMETER;
226 }
227 }
228
229 m_quantParams = quantParams;
230 return OH_NN_SUCCESS;
231 }
232
IdentifyOpParameter()233 void NNTensor::IdentifyOpParameter()
234 {
235 m_isOpParameter = true;
236 }
237
SetName(const std::string & name)238 void NNTensor::SetName(const std::string& name)
239 {
240 m_name = name;
241 }
242
243 // Buffer set inside NNTensor will be released during deconstruction, make sure the buffer won't be released twice.
SetBuffer(const void * buffer,size_t length)244 void NNTensor::SetBuffer(const void* buffer, size_t length)
245 {
246 // copy pointer instead of memory copying
247 m_buffer = const_cast<void*>(buffer);
248 m_bufferLength = length;
249 }
250
SetDimensions(const std::vector<int32_t> & dimensions)251 OH_NN_ReturnCode NNTensor::SetDimensions(const std::vector<int32_t>& dimensions)
252 {
253 size_t expectedDimensionCount = m_dimensions.size();
254 size_t dimensionCount = dimensions.size();
255 if (dimensionCount != expectedDimensionCount) {
256 LOGE("Passed dimensions have different dimension counts from NNTensor, expected %zu, but passed %zu.",
257 expectedDimensionCount, dimensionCount);
258 return OH_NN_INVALID_PARAMETER;
259 }
260
261 auto ret = ParseDimensions(dimensions);
262 if (ret != OH_NN_SUCCESS) {
263 LOGE("SetDimemsions failed, passed invalid dimension info.");
264 return ret;
265 }
266
267 m_dimensions = dimensions;
268 return OH_NN_SUCCESS;
269 }
270
GetType() const271 OH_NN_TensorType NNTensor::GetType() const
272 {
273 return m_type;
274 }
275
GetName() const276 std::string NNTensor::GetName() const
277 {
278 return m_name;
279 }
280
GetBuffer() const281 void* NNTensor::GetBuffer() const
282 {
283 return m_buffer;
284 }
285
GetBufferLength() const286 size_t NNTensor::GetBufferLength() const
287 {
288 return m_bufferLength;
289 }
290
GetDataLength() const291 size_t NNTensor::GetDataLength() const
292 {
293 return m_dataLength;
294 }
295
GetDataType() const296 OH_NN_DataType NNTensor::GetDataType() const
297 {
298 return m_dataType;
299 }
300
GetElementCount() const301 uint32_t NNTensor::GetElementCount() const
302 {
303 return m_elementCount;
304 }
305
GetDimensions() const306 std::vector<int32_t> NNTensor::GetDimensions() const
307 {
308 return m_dimensions;
309 }
310
GetFormat() const311 OH_NN_Format NNTensor::GetFormat() const
312 {
313 return m_format;
314 }
315
GetQuantParam() const316 std::vector<QuantParam> NNTensor::GetQuantParam() const
317 {
318 return m_quantParams;
319 }
320
ConvertToLiteGraphTensor() const321 LiteGraphTensorPtr NNTensor::ConvertToLiteGraphTensor() const
322 {
323 mindspore::lite::DataType dataType = NNToMS::TransformDataType(m_dataType);
324 mindspore::lite::Format format = NNToMS::TransformFormat(m_format);
325 const uint8_t* buffer = static_cast<const uint8_t*>(m_buffer);
326 std::vector<uint8_t> data = ConstructVectorFromArray(buffer, m_dataLength);
327
328 std::vector<mindspore::lite::QuantParam> quantParams;
329 mindspore::lite::QuantParam msQuantParam;
330 for (const QuantParam& param : m_quantParams) {
331 msQuantParam = {param.zeroPoint, param.scale, param.numBits};
332 quantParams.emplace_back(std::move(msQuantParam));
333 }
334
335 mindspore::lite::TensorPtr tensor = mindspore::lite::MindIR_Tensor_Create(
336 m_name, dataType, m_dimensions, format, data, quantParams);
337 if (tensor == nullptr) {
338 LOGE("ConvertToLiteGraphTensor failed, please check attributes of NNTensor.");
339 return {nullptr, DestroyLiteGraphTensor};
340 }
341
342 LiteGraphTensorPtr liteGraphTensor(tensor, DestroyLiteGraphTensor);
343 return liteGraphTensor;
344 }
345
ConvertToIOTensor(IOTensor & tensor) const346 void NNTensor::ConvertToIOTensor(IOTensor& tensor) const
347 {
348 tensor.dataType = m_dataType;
349 tensor.format = m_format;
350 tensor.dimensions = m_dimensions;
351 tensor.data = const_cast<void*>(m_buffer);
352 tensor.length = m_bufferLength;
353 }
354
IsDynamicShape() const355 bool NNTensor::IsDynamicShape() const
356 {
357 return m_isDynamicShape;
358 }
359
IsQuantTensor() const360 bool NNTensor::IsQuantTensor() const
361 {
362 return (m_quantParams.size() > 0);
363 }
364
IsScalar() const365 bool NNTensor::IsScalar() const
366 {
367 return (m_dimensions.empty());
368 }
369
IsOpParameter() const370 bool NNTensor::IsOpParameter() const
371 {
372 return m_isOpParameter;
373 }
374
CompareAttribute(const NNTensor & tensor) const375 bool NNTensor::CompareAttribute(const NNTensor& tensor) const
376 {
377 if (m_dataType != tensor.GetDataType()) {
378 LOGI("Tensors have different data type: %d and %d.", m_dataType, tensor.GetDataType());
379 return false;
380 }
381
382 if (m_format != tensor.GetFormat()) {
383 LOGI("Tensors have different format: %d and %d.", m_format, tensor.GetFormat());
384 return false;
385 }
386
387 const std::vector<int32_t> dimensions = tensor.GetDimensions();
388 if (m_dimensions.size() != dimensions.size()) {
389 LOGI("Tensors have differents dimension counts: %zu and %zu.", m_dimensions.size(), dimensions.size());
390 return false;
391 }
392
393 for (auto i = 0; i < dimensions.size(); i++) {
394 if (m_dimensions[i] != -1 && m_dimensions[i] != dimensions[i]) {
395 LOGI("Tensors have different dimension: dimension index: %u, dimension value: %d and %d.",
396 i, m_dimensions[i], dimensions[i]);
397 return false;
398 }
399 }
400
401 if (m_type != tensor.GetType()) {
402 LOGI("Tensors have different type: %d and %d.", m_type, tensor.GetType());
403 return false;
404 }
405
406 return true;
407 }
408 } // NeuralNetworkRuntime
409 } // OHOS