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 #include "quant_param.h"
27 
28 namespace OHOS {
29 namespace NeuralNetworkRuntime {
30 const uint32_t SUPPORT_NUM_BIT = 8; // Currently support 8-bit quantization only
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> & quantParams,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>& quantParams,
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 returnCode = ValidateDimensions(dimensions);
89     if (returnCode != OH_NN_SUCCESS) {
90         LOGE("Build failed, error happened when validating dimensions.");
91         return returnCode;
92     }
93     m_dimensions = dimensions;
94 
95     returnCode = ValidateQuantParams(quantParams);
96     if (returnCode != OH_NN_SUCCESS) {
97         LOGE("Build failed, error happened when validating quantParams.");
98         return returnCode;
99     }
100     m_quantParams = quantParams;
101 
102     return OH_NN_SUCCESS;
103 }
104 
BuildFromOHNNTensor(const OH_NN_Tensor & nnTensor)105 OH_NN_ReturnCode NNTensor::BuildFromOHNNTensor(const OH_NN_Tensor& nnTensor)
106 {
107     m_type = nnTensor.type;
108 
109     if (!Validation::ValidateTensorDataType(nnTensor.dataType)) {
110         LOGE("BuildFromOHNNTensor failed, passed invalid data type: %d.", nnTensor.dataType);
111         return OH_NN_INVALID_PARAMETER;
112     }
113     m_dataType = nnTensor.dataType;
114 
115     if (!Validation::ValidateTensorType(nnTensor.type)) {
116         LOGE("BuildFromOHNNTensor failed, passed invalid nnTensor type: %d.", nnTensor.type);
117         return OH_NN_INVALID_PARAMETER;
118     }
119 
120     OH_NN_ReturnCode returnCode = ParseDimensions(nnTensor.dimensions, nnTensor.dimensionCount);
121     if (returnCode != OH_NN_SUCCESS) {
122         LOGE("BuildFromOHNNTensor failed, passed invalid nnTensor dimensions.");
123         return returnCode;
124     }
125 
126     returnCode = ParseQuantParams(nnTensor.quantParam);
127     if (returnCode != OH_NN_SUCCESS) {
128         LOGE("BuildFromOHNNTensor failed, please check quantParam in nnTensor.");
129         return returnCode;
130     }
131 
132     return OH_NN_SUCCESS;
133 }
134 
BuildFromOHNNTensorInfo(const OH_NN_TensorInfo & nnTensorInfo)135 OH_NN_ReturnCode NNTensor::BuildFromOHNNTensorInfo(const OH_NN_TensorInfo& nnTensorInfo)
136 {
137     if (!Validation::ValidateTensorDataType(nnTensorInfo.dataType)) {
138         LOGE("BuildFromOHNNTensorInfo failed, passed invalid data type: %d.", nnTensorInfo.dataType);
139         return OH_NN_INVALID_PARAMETER;
140     }
141     m_dataType = nnTensorInfo.dataType;
142 
143     if (!Validation::ValidateTensorFormat(nnTensorInfo.format)) {
144         LOGE("BuildFromOHNNTensorInfo failed, passed invalid nnTensorInfo format: %d.", nnTensorInfo.format);
145         return OH_NN_INVALID_PARAMETER;
146     }
147     m_format = nnTensorInfo.format;
148     m_name = nnTensorInfo.name;
149 
150     OH_NN_ReturnCode returnCode = ParseDimensions(nnTensorInfo.dimensions, nnTensorInfo.dimensionCount);
151     if (returnCode != OH_NN_SUCCESS) {
152         LOGE("BuildFromOHNNTensorInfo failed, passed invalid nnTensorInfo dimensions.");
153         return returnCode;
154     }
155 
156     return OH_NN_SUCCESS;
157 }
158 
BuildFromTensorDesc(const NN_TensorDesc * tensorDesc)159 OH_NN_ReturnCode NNTensor::BuildFromTensorDesc(const NN_TensorDesc* tensorDesc)
160 {
161     if (tensorDesc == nullptr) {
162         LOGE("BuildFromTensorDesc failed, passed nullptr to tensorDesc.");
163         return OH_NN_INVALID_PARAMETER;
164     }
165 
166     const auto* tensorDescImpl = reinterpret_cast<const OHOS::NeuralNetworkRuntime::TensorDesc*>(tensorDesc);
167 
168     // Get datatype from TensorDesc
169     OH_NN_DataType dataType;
170     OH_NN_ReturnCode returnCode = tensorDescImpl->GetDataType(&dataType);
171     if (returnCode != OH_NN_SUCCESS) {
172         LOGE("BuildFromTensorDesc failed, error happened when get dataType.");
173         return returnCode;
174     }
175     if (!OHOS::NeuralNetworkRuntime::Validation::ValidateTensorDataType(dataType)) {
176         LOGE("BuildFromTensorDesc failed, passed invalid dataType.");
177         return OH_NN_INVALID_PARAMETER;
178     }
179 
180     // Get Dimensions from TensorDesc and transform to std::vector
181     int32_t* shape {nullptr};
182     size_t shapeNum {0};
183     returnCode = tensorDescImpl->GetShape(&shape, &shapeNum);
184     if (returnCode != OH_NN_SUCCESS) {
185         LOGE("BuildFromTensorDesc failed, error happened when get shape.");
186         return returnCode;
187     }
188     std::vector<int32_t> dimensions(shape, shape + shapeNum);
189 
190     // OH_NNCore_TensorDesc does not include quant parameters and tensor type,
191     // should be setted by using indenpendent interface.
192     returnCode = Build(dataType, dimensions, {}, OH_NN_TENSOR);
193     if (returnCode != OH_NN_SUCCESS) {
194         LOGE("BuildFromTensorDesc failed, error happened when building NNTensor.");
195     }
196 
197     return returnCode;
198 }
199 
SetQuantParam(const NN_QuantParam * quantParam)200 OH_NN_ReturnCode NNTensor::SetQuantParam(const NN_QuantParam* quantParam)
201 {
202     if (quantParam == nullptr) {
203         LOGE("SetQuantParam failed, quantParam is nullptr.");
204         return OH_NN_INVALID_PARAMETER;
205     }
206 
207     const auto* quantParamImpl = reinterpret_cast<const OHOS::NeuralNetworkRuntime::QuantParams*>(quantParam);
208     m_quantParams.clear();
209     OH_NN_ReturnCode returnCode = quantParamImpl->CopyToCompat(m_quantParams);
210     if (returnCode != OH_NN_SUCCESS) {
211         LOGE("SetQuantParam failed, error happened when converting quantization parameters.");
212         return returnCode;
213     }
214 
215     returnCode = ValidateQuantParams(m_quantParams);
216     if (returnCode != OH_NN_SUCCESS) {
217         m_quantParams.clear();
218         LOGE("SetQuantParam failed, error happened when parsing quantization parameters.");
219     }
220 
221     return returnCode;
222 }
223 
SetTensorType(OH_NN_TensorType tensorType)224 OH_NN_ReturnCode NNTensor::SetTensorType(OH_NN_TensorType tensorType)
225 {
226     m_type = tensorType;
227     return OH_NN_SUCCESS;
228 }
229 
ValidateDimensions(const std::vector<int32_t> & dimensions)230 OH_NN_ReturnCode NNTensor::ValidateDimensions(const std::vector<int32_t>& dimensions)
231 {
232     // Temporary variable to check overflow.
233     uint64_t absoluteDim {0};
234     uint64_t elementCount {1};
235     uint64_t dataLength {static_cast<uint64_t>(GetTypeSize(m_dataType))};
236     m_isDynamicShape = false;
237     for (int32_t dim : dimensions) {
238         if (dim < -1 || dim == 0) {
239             LOGE("ParseDimension failed, dimension of OH_NN_Tensor cannot be 0 or less than -1, receive %d.", dim);
240             return OH_NN_INVALID_PARAMETER;
241         }
242 
243         m_isDynamicShape = m_isDynamicShape || (dim == -1);
244         absoluteDim = static_cast<uint64_t>(abs(dim));
245         elementCount *= absoluteDim;
246         dataLength *= absoluteDim;
247 
248         if (dataLength > UINT32_MAX) {
249             LOGE("ParseDimension failed, expected data length of tensor exceed limit %u.", UINT32_MAX);
250             return OH_NN_INVALID_PARAMETER;
251         }
252     }
253 
254     if (m_isDynamicShape) {
255         // If tensor has dynamic shape, m_elementCount and m_dataLength take 0.
256         m_elementCount = 0;
257         m_dataLength = 0;
258     } else {
259         m_elementCount = static_cast<uint32_t>(elementCount);
260         m_dataLength = static_cast<size_t>(dataLength);
261     }
262 
263     return OH_NN_SUCCESS;
264 }
265 
ParseDimensions(const int32_t * dimensions,uint32_t dimensionCount)266 OH_NN_ReturnCode NNTensor::ParseDimensions(const int32_t* dimensions, uint32_t dimensionCount)
267 {
268     OH_NN_ReturnCode returnCode = Validation::ValidateArray(dimensions, dimensionCount);
269     if (returnCode != OH_NN_SUCCESS) {
270         LOGE("BuildFromOHNNTensor failed, please check dimension and dimensionCount in NNTensor.");
271         return returnCode;
272     }
273     std::vector<int32_t> dimensionsVec = ConstructVectorFromArray(dimensions, dimensionCount);
274 
275     returnCode = ValidateDimensions(dimensionsVec);
276     if (returnCode != OH_NN_SUCCESS) {
277         LOGE("BuildFromOHNNTensor failed, passed invalid dimension info.");
278         return returnCode;
279     }
280     m_dimensions = std::move(dimensionsVec);
281 
282     return OH_NN_SUCCESS;
283 }
284 
ParseQuantParams(const OH_NN_QuantParam * quantParam)285 OH_NN_ReturnCode NNTensor::ParseQuantParams(const OH_NN_QuantParam* quantParam)
286 {
287     if (quantParam == nullptr) {
288         return OH_NN_SUCCESS;
289     }
290 
291     if ((quantParam->numBits == nullptr) || (quantParam->scale == nullptr) || (quantParam->zeroPoint == nullptr)) {
292         LOGE("ParseQuantParams failed, scale or zeroPoint is nullptr.");
293         return OH_NN_INVALID_PARAMETER;
294     }
295 
296     std::vector<QuantParam> tmpQuantParam;
297     uint32_t numBits{0};
298     double scale{0.0};
299     int32_t zeroPoint{0};
300     for (uint32_t i = 0; i < quantParam->quantCount; i++) {
301         numBits = quantParam->numBits[i];
302         scale = quantParam->scale[i];
303         zeroPoint = quantParam->zeroPoint[i];
304         tmpQuantParam.emplace_back((QuantParam){numBits, scale, zeroPoint});
305     }
306 
307     OH_NN_ReturnCode returnCode = ValidateQuantParams(tmpQuantParam);
308     if (returnCode != OH_NN_SUCCESS) {
309         LOGE("ParseQuantParams failed, error happened when validating quantization parameters.");
310         return returnCode;
311     }
312     m_quantParams = std::move(tmpQuantParam);
313 
314     return OH_NN_SUCCESS;
315 }
316 
ValidateQuantParams(const std::vector<QuantParam> & quantParams)317 OH_NN_ReturnCode NNTensor::ValidateQuantParams(const std::vector<QuantParam>& quantParams)
318 {
319     // Only support 8-bit quantization in NNR version 1.0
320     auto paramIt = std::find_if(quantParams.begin(), quantParams.end(), [](QuantParam quant) {
321         return  quant.numBits != SUPPORT_NUM_BIT;
322     });
323     if (paramIt != quantParams.end()) {
324             LOGE("ValidateQuantParams failed, get invalid numBits %d.", paramIt->numBits);
325             return OH_NN_INVALID_PARAMETER;
326     }
327 
328     return OH_NN_SUCCESS;
329 }
330 
IdentifyOpParameter()331 void NNTensor::IdentifyOpParameter()
332 {
333     m_isOpParameter = true;
334 }
335 
SetName(const std::string & name)336 void NNTensor::SetName(const std::string& name)
337 {
338     m_name = name;
339 }
340 
341 // 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)342 void NNTensor::SetBuffer(const void* buffer, size_t length)
343 {
344     // copy pointer instead of memory copying
345     m_buffer = const_cast<void*>(buffer);
346     m_bufferLength = length;
347 }
348 
SetFormat(const OH_NN_Format & format)349 void NNTensor::SetFormat(const OH_NN_Format& format)
350 {
351     m_format = format;
352 }
353 
SetDimensions(const std::vector<int32_t> & dimensions)354 OH_NN_ReturnCode NNTensor::SetDimensions(const std::vector<int32_t>& dimensions)
355 {
356     size_t expectedDimensionCount = m_dimensions.size();
357     size_t dimensionCount = dimensions.size();
358     if (dimensionCount != expectedDimensionCount) {
359         LOGE("Passed dimensions have different dimension counts from NNTensor, expected %zu, but passed %zu.",
360              expectedDimensionCount, dimensionCount);
361         return OH_NN_INVALID_PARAMETER;
362     }
363 
364     auto returnCode = ValidateDimensions(dimensions);
365     if (returnCode != OH_NN_SUCCESS) {
366         LOGE("SetDimemsions failed, error happened when validating dimensions.");
367         return returnCode;
368     }
369 
370     m_dimensions = dimensions;
371     return OH_NN_SUCCESS;
372 }
373 
GetType() const374 OH_NN_TensorType NNTensor::GetType() const
375 {
376     return m_type;
377 }
378 
GetName() const379 std::string NNTensor::GetName() const
380 {
381     return m_name;
382 }
383 
GetBuffer() const384 void* NNTensor::GetBuffer() const
385 {
386     return m_buffer;
387 }
388 
GetBufferLength() const389 size_t NNTensor::GetBufferLength() const
390 {
391     return m_bufferLength;
392 }
393 
GetDataLength() const394 size_t NNTensor::GetDataLength() const
395 {
396     return m_dataLength;
397 }
398 
GetDataType() const399 OH_NN_DataType NNTensor::GetDataType() const
400 {
401     return m_dataType;
402 }
403 
GetElementCount() const404 uint32_t NNTensor::GetElementCount() const
405 {
406     return m_elementCount;
407 }
408 
GetDimensions() const409 std::vector<int32_t> NNTensor::GetDimensions() const
410 {
411     return m_dimensions;
412 }
413 
GetFormat() const414 OH_NN_Format NNTensor::GetFormat() const
415 {
416     return m_format;
417 }
418 
GetQuantParam() const419 std::vector<QuantParam> NNTensor::GetQuantParam() const
420 {
421     return m_quantParams;
422 }
423 
ConvertToLiteGraphTensor() const424 LiteGraphTensorPtr NNTensor::ConvertToLiteGraphTensor() const
425 {
426     mindspore::lite::DataType dataType = NNToMS::TransformDataType(m_dataType);
427     mindspore::lite::Format format = NNToMS::TransformFormat(m_format);
428     const uint8_t* buffer = static_cast<const uint8_t*>(m_buffer);
429     std::vector<uint8_t> data = ConstructVectorFromArray(buffer, m_dataLength);
430 
431     std::vector<mindspore::lite::QuantParam> quantParams;
432     mindspore::lite::QuantParam msQuantParam;
433     for (const QuantParam& param : m_quantParams) {
434         msQuantParam = {param.zeroPoint, param.scale, param.numBits};
435         quantParams.emplace_back(std::move(msQuantParam));
436     }
437 
438     mindspore::lite::TensorPtr tensor = mindspore::lite::MindIR_Tensor_Create(
439         m_name.c_str(), dataType, m_dimensions.data(), m_dimensions.size(), format,
440         data.data(), data.size(), quantParams.data(), quantParams.size());
441     if (tensor == nullptr) {
442         LOGE("ConvertToLiteGraphTensor failed, please check attributes of NNTensor.");
443         return {nullptr, DestroyLiteGraphTensor};
444     }
445 
446     LiteGraphTensorPtr liteGraphTensor(tensor, DestroyLiteGraphTensor);
447     return liteGraphTensor;
448 }
449 
ConvertToIOTensor(IOTensor & tensor) const450 void NNTensor::ConvertToIOTensor(IOTensor& tensor) const
451 {
452     tensor.dataType = m_dataType;
453     tensor.format = m_format;
454     tensor.dimensions = m_dimensions;
455     tensor.data = const_cast<void*>(m_buffer);
456     tensor.length = m_bufferLength;
457 }
458 
ConvertToTensorDesc(TensorDesc & desc) const459 void NNTensor::ConvertToTensorDesc(TensorDesc& desc) const
460 {
461     desc.SetDataType(m_dataType);
462     desc.SetFormat(m_format);
463     desc.SetName(m_name.c_str());
464     desc.SetShape(m_dimensions.data(), m_dimensions.size());
465 }
466 
IsDynamicShape() const467 bool NNTensor::IsDynamicShape() const
468 {
469     return m_isDynamicShape;
470 }
471 
IsQuantTensor() const472 bool NNTensor::IsQuantTensor() const
473 {
474     return (m_quantParams.size() > 0);
475 }
476 
IsScalar() const477 bool NNTensor::IsScalar() const
478 {
479     return (m_dimensions.empty());
480 }
481 
IsOpParameter() const482 bool NNTensor::IsOpParameter() const
483 {
484     return m_isOpParameter;
485 }
486 
CompareAttribute(const NNTensor & tensor) const487 bool NNTensor::CompareAttribute(const NNTensor& tensor) const
488 {
489     if (m_dataType != tensor.GetDataType()) {
490         LOGI("Tensors have different data type: %d and %d.", m_dataType, tensor.GetDataType());
491         return false;
492     }
493 
494     if (m_format != tensor.GetFormat()) {
495         LOGI("Tensors have different format: %d and %d.", m_format, tensor.GetFormat());
496         return false;
497     }
498 
499     const std::vector<int32_t> dimensions = tensor.GetDimensions();
500     if (m_dimensions.size() != dimensions.size()) {
501         LOGI("Tensors have differents dimension counts: %zu and %zu.", m_dimensions.size(), dimensions.size());
502         return false;
503     }
504 
505     size_t dimensionsSize = dimensions.size();
506     for (size_t i = 0; i < dimensionsSize; i++) {
507         if ((m_dimensions[i] != -1) && (m_dimensions[i] != dimensions[i])) {
508             LOGI("Tensors have different dimension: dimension index: %zu, dimension value: %d and %d.",
509                  i, m_dimensions[i], dimensions[i]);
510             return false;
511         }
512     }
513 
514     if (m_type != tensor.GetType()) {
515         LOGI("Tensors have different type: %d and %d.", m_type, tensor.GetType());
516         return false;
517     }
518 
519     return true;
520 }
521 } // NeuralNetworkRuntime
522 } // OHOS
523