• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.dimensions, nnTensor.dimensionCount);
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 
BuildFromOHNNTensorInfo(const OH_NN_TensorInfo & nnTensorInfo)133 OH_NN_ReturnCode NNTensor::BuildFromOHNNTensorInfo(const OH_NN_TensorInfo& nnTensorInfo)
134 {
135     if (!Validation::ValidateTensorDataType(nnTensorInfo.dataType)) {
136         LOGE("BuildFromOHNNTensorInfo failed, passed invalid data type: %d.", nnTensorInfo.dataType);
137         return OH_NN_INVALID_PARAMETER;
138     }
139     m_dataType = nnTensorInfo.dataType;
140 
141     if (!Validation::ValidateTensorFormat(nnTensorInfo.format)) {
142         LOGE("BuildFromOHNNTensorInfo failed, passed invalid nnTensorInfo format: %d.", nnTensorInfo.format);
143         return OH_NN_INVALID_PARAMETER;
144     }
145     m_format = nnTensorInfo.format;
146     m_name = nnTensorInfo.name;
147 
148     OH_NN_ReturnCode ret = ParseDimensions(nnTensorInfo.dimensions, nnTensorInfo.dimensionCount);
149     if (ret != OH_NN_SUCCESS) {
150         LOGE("BuildFromOHNNTensorInfo failed, passed invalid nnTensorInfo dimensions.");
151         return ret;
152     }
153 
154     return OH_NN_SUCCESS;
155 }
156 
ParseDimensions(const std::vector<int32_t> & dimensions)157 OH_NN_ReturnCode NNTensor::ParseDimensions(const std::vector<int32_t>& dimensions)
158 {
159     // Temporary variable to check overflow.
160     uint64_t absoluteDim {0};
161     uint64_t elementCount {1};
162     uint64_t dataLength {static_cast<uint64_t>(GetTypeSize(m_dataType))};
163     m_isDynamicShape = false;
164     for (int32_t dim : dimensions) {
165         if (dim < -1 || dim == 0) {
166             LOGE("ParseDimension failed, dimension of OH_NN_Tensor cannot be 0 or less than -1, receive %d.", dim);
167             return OH_NN_INVALID_PARAMETER;
168         }
169 
170         m_isDynamicShape = m_isDynamicShape || (dim == -1);
171         absoluteDim = static_cast<uint64_t>(abs(dim));
172         elementCount *= absoluteDim;
173         dataLength *= absoluteDim;
174 
175         if (dataLength > UINT32_MAX) {
176             LOGE("ParseDimension failed, expected data length of tensor exceed limit %u.", UINT32_MAX);
177             return OH_NN_INVALID_PARAMETER;
178         }
179     }
180 
181     if (m_isDynamicShape) {
182         // If tensor has dynamic shape, m_elementCount and m_dataLength take 0.
183         m_elementCount = 0;
184         m_dataLength = 0;
185     } else {
186         m_elementCount = static_cast<uint32_t>(elementCount);
187         m_dataLength = static_cast<size_t>(dataLength);
188     }
189 
190     m_dimensions = std::move(dimensions);
191     return OH_NN_SUCCESS;
192 }
193 
ParseDimensions(const int32_t * dimensions,uint32_t dimensionCount)194 OH_NN_ReturnCode NNTensor::ParseDimensions(const int32_t* dimensions, uint32_t dimensionCount)
195 {
196     OH_NN_ReturnCode ret = Validation::ValidateArray(dimensions, dimensionCount);
197     if (ret != OH_NN_SUCCESS) {
198         LOGE("BuildFromOHNNTensor failed, please check dimension and dimensionCount in NNTensor.");
199         return ret;
200     }
201     std::vector<int32_t> dimensionsVec = ConstructVectorFromArray(dimensions, dimensionCount);
202 
203     ret = ParseDimensions(dimensionsVec);
204     if (ret != OH_NN_SUCCESS) {
205         LOGE("BuildFromOHNNTensor failed, passed invalid dimension info.");
206         return ret;
207     }
208 
209     return OH_NN_SUCCESS;
210 }
211 
ParseQuantParams(const OH_NN_QuantParam * quantParam)212 OH_NN_ReturnCode NNTensor::ParseQuantParams(const OH_NN_QuantParam* quantParam)
213 {
214     if (quantParam == nullptr) {
215         return OH_NN_SUCCESS;
216     }
217 
218     if ((quantParam->numBits == nullptr) || (quantParam->scale == nullptr) || (quantParam->zeroPoint == nullptr)) {
219         LOGE("ParseQuantParams failed, scale or zeroPoint is nullptr.");
220         return OH_NN_INVALID_PARAMETER;
221     }
222 
223     std::vector<QuantParam> tmpQuantParam;
224     uint32_t numBits{0};
225     double scale{0.0};
226     int32_t zeroPoint{0};
227     for (uint32_t i = 0; i < quantParam->quantCount; i++) {
228         numBits = quantParam->numBits[i];
229         scale = quantParam->scale[i];
230         zeroPoint = quantParam->zeroPoint[i];
231         tmpQuantParam.emplace_back((QuantParam){numBits, scale, zeroPoint});
232     }
233 
234     OH_NN_ReturnCode ret = ParseQuantParams(tmpQuantParam);
235     if (ret != OH_NN_SUCCESS) {
236         LOGE("ParseQuantParams failed, please numBits in NNTensor.");
237         return ret;
238     }
239 
240     return OH_NN_SUCCESS;
241 }
242 
ParseQuantParams(const std::vector<QuantParam> & quantParams)243 OH_NN_ReturnCode NNTensor::ParseQuantParams(const std::vector<QuantParam>& quantParams)
244 {
245     for (const QuantParam& param : quantParams) {
246         // Only support 8-bit quantization in NNR version 1.0
247         if ((param.numBits != SUPPORT_NUM_BIT) || (param.numBits == INVALID_NUM_BIT)) {
248             LOGE("ParseQuantParams failed, get invalid numBits %d.", param.numBits);
249             return OH_NN_INVALID_PARAMETER;
250         }
251     }
252 
253     m_quantParams = quantParams;
254     return OH_NN_SUCCESS;
255 }
256 
IdentifyOpParameter()257 void NNTensor::IdentifyOpParameter()
258 {
259     m_isOpParameter = true;
260 }
261 
SetName(const std::string & name)262 void NNTensor::SetName(const std::string& name)
263 {
264     m_name = name;
265 }
266 
267 // 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)268 void NNTensor::SetBuffer(const void* buffer, size_t length)
269 {
270     // copy pointer instead of memory copying
271     m_buffer = const_cast<void*>(buffer);
272     m_bufferLength = length;
273 }
274 
SetFormat(const OH_NN_Format & format)275 void NNTensor::SetFormat(const OH_NN_Format& format)
276 {
277     m_format = format;
278 }
279 
SetDimensions(const std::vector<int32_t> & dimensions)280 OH_NN_ReturnCode NNTensor::SetDimensions(const std::vector<int32_t>& dimensions)
281 {
282     size_t expectedDimensionCount = m_dimensions.size();
283     size_t dimensionCount = dimensions.size();
284     if (dimensionCount != expectedDimensionCount) {
285         LOGE("Passed dimensions have different dimension counts from NNTensor, expected %zu, but passed %zu.",
286              expectedDimensionCount, dimensionCount);
287         return OH_NN_INVALID_PARAMETER;
288     }
289 
290     auto ret = ParseDimensions(dimensions);
291     if (ret != OH_NN_SUCCESS) {
292         LOGE("SetDimemsions failed, passed invalid dimension info.");
293         return ret;
294     }
295 
296     m_dimensions = dimensions;
297     return OH_NN_SUCCESS;
298 }
299 
GetType() const300 OH_NN_TensorType NNTensor::GetType() const
301 {
302     return m_type;
303 }
304 
GetName() const305 std::string NNTensor::GetName() const
306 {
307     return m_name;
308 }
309 
GetBuffer() const310 void* NNTensor::GetBuffer() const
311 {
312     return m_buffer;
313 }
314 
GetBufferLength() const315 size_t NNTensor::GetBufferLength() const
316 {
317     return m_bufferLength;
318 }
319 
GetDataLength() const320 size_t NNTensor::GetDataLength() const
321 {
322     return m_dataLength;
323 }
324 
GetDataType() const325 OH_NN_DataType NNTensor::GetDataType() const
326 {
327     return m_dataType;
328 }
329 
GetElementCount() const330 uint32_t NNTensor::GetElementCount() const
331 {
332     return m_elementCount;
333 }
334 
GetDimensions() const335 std::vector<int32_t> NNTensor::GetDimensions() const
336 {
337     return m_dimensions;
338 }
339 
GetFormat() const340 OH_NN_Format NNTensor::GetFormat() const
341 {
342     return m_format;
343 }
344 
GetQuantParam() const345 std::vector<QuantParam> NNTensor::GetQuantParam() const
346 {
347     return m_quantParams;
348 }
349 
ConvertToLiteGraphTensor() const350 LiteGraphTensorPtr NNTensor::ConvertToLiteGraphTensor() const
351 {
352     mindspore::lite::DataType dataType = NNToMS::TransformDataType(m_dataType);
353     mindspore::lite::Format format = NNToMS::TransformFormat(m_format);
354     const uint8_t* buffer = static_cast<const uint8_t*>(m_buffer);
355     std::vector<uint8_t> data = ConstructVectorFromArray(buffer, m_dataLength);
356 
357     std::vector<mindspore::lite::QuantParam> quantParams;
358     mindspore::lite::QuantParam msQuantParam;
359     for (const QuantParam& param : m_quantParams) {
360         msQuantParam = {param.zeroPoint, param.scale, param.numBits};
361         quantParams.emplace_back(std::move(msQuantParam));
362     }
363 
364     mindspore::lite::TensorPtr tensor = mindspore::lite::MindIR_Tensor_Create(
365         m_name, dataType, m_dimensions, format, data, quantParams);
366     if (tensor == nullptr) {
367         LOGE("ConvertToLiteGraphTensor failed, please check attributes of NNTensor.");
368         return {nullptr, DestroyLiteGraphTensor};
369     }
370 
371     LiteGraphTensorPtr liteGraphTensor(tensor, DestroyLiteGraphTensor);
372     return liteGraphTensor;
373 }
374 
ConvertToIOTensor(IOTensor & tensor) const375 void NNTensor::ConvertToIOTensor(IOTensor& tensor) const
376 {
377     tensor.dataType = m_dataType;
378     tensor.format = m_format;
379     tensor.dimensions = m_dimensions;
380     tensor.data = const_cast<void*>(m_buffer);
381     tensor.length = m_bufferLength;
382 }
383 
IsDynamicShape() const384 bool NNTensor::IsDynamicShape() const
385 {
386     return m_isDynamicShape;
387 }
388 
IsQuantTensor() const389 bool NNTensor::IsQuantTensor() const
390 {
391     return (m_quantParams.size() > 0);
392 }
393 
IsScalar() const394 bool NNTensor::IsScalar() const
395 {
396     return (m_dimensions.empty());
397 }
398 
IsOpParameter() const399 bool NNTensor::IsOpParameter() const
400 {
401     return m_isOpParameter;
402 }
403 
CompareAttribute(const NNTensor & tensor) const404 bool NNTensor::CompareAttribute(const NNTensor& tensor) const
405 {
406     if (m_dataType != tensor.GetDataType()) {
407         LOGI("Tensors have different data type: %d and %d.", m_dataType, tensor.GetDataType());
408         return false;
409     }
410 
411     if (m_format != tensor.GetFormat()) {
412         LOGI("Tensors have different format: %d and %d.", m_format, tensor.GetFormat());
413         return false;
414     }
415 
416     const std::vector<int32_t> dimensions = tensor.GetDimensions();
417     if (m_dimensions.size() != dimensions.size()) {
418         LOGI("Tensors have differents dimension counts: %zu and %zu.", m_dimensions.size(), dimensions.size());
419         return false;
420     }
421 
422     size_t dimensionsSize = dimensions.size();
423     for (size_t i = 0; i < dimensionsSize; i++) {
424         if ((m_dimensions[i] != -1) && (m_dimensions[i] != dimensions[i])) {
425             LOGI("Tensors have different dimension: dimension index: %zu, dimension value: %d and %d.",
426                  i, m_dimensions[i], dimensions[i]);
427             return false;
428         }
429     }
430 
431     if (m_type != tensor.GetType()) {
432         LOGI("Tensors have different type: %d and %d.", m_type, tensor.GetType());
433         return false;
434     }
435 
436     return true;
437 }
438 } // NeuralNetworkRuntime
439 } // OHOS