• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright 2022 Huawei Technologies Co., Ltd
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "include/api/types.h"
17 #include "include/api/data_type.h"
18 #include "include/api/format.h"
19 #include "src/common/log_adapter.h"
20 #include "src/litert/cxx_api/tensor_utils.h"
21 #include "third_party/securec/include/securec.h"
22 #include "mindspore/lite/src/common/mutable_tensor_impl.h"
23 #include "mindspore/lite/python/src/tensor_numpy_impl.h"
24 #include "mindspore/core/ir/api_tensor_impl.h"
25 #include "pybind11/pybind11.h"
26 #include "pybind11/numpy.h"
27 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
28 #include "numpy/arrayobject.h"
29 #include "pybind11/stl.h"
30 #ifdef ENABLE_CLOUD_INFERENCE
31 #include "extendrt/kernel/ascend/plugin/ascend_allocator_plugin.h"
32 #endif
33 namespace mindspore::lite {
34 namespace {
IsCContiguous(const py::array & input)35 bool IsCContiguous(const py::array &input) {
36   auto flags = static_cast<unsigned int>(input.flags());
37   return (flags & pybind11::detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_) != 0;
38 }
39 }  // namespace
40 
41 namespace py = pybind11;
42 using MSTensorPtr = std::shared_ptr<MSTensor>;
43 
44 py::buffer_info GetPyBufferInfo(const MSTensorPtr &tensor);
45 bool SetTensorNumpyData(const MSTensorPtr &tensor, const py::array &input);
46 
TensorPyBind(const py::module & m)47 void TensorPyBind(const py::module &m) {
48   (void)py::enum_<DataType>(m, "DataType")
49     .value("kTypeUnknown", DataType::kTypeUnknown)
50     .value("kObjectTypeString", DataType::kObjectTypeString)
51     .value("kObjectTypeList", DataType::kObjectTypeList)
52     .value("kObjectTypeTuple", DataType::kObjectTypeTuple)
53     .value("kObjectTypeTensorType", DataType::kObjectTypeTensorType)
54     .value("kNumberTypeBool", DataType::kNumberTypeBool)
55     .value("kNumberTypeInt8", DataType::kNumberTypeInt8)
56     .value("kNumberTypeInt16", DataType::kNumberTypeInt16)
57     .value("kNumberTypeInt32", DataType::kNumberTypeInt32)
58     .value("kNumberTypeInt64", DataType::kNumberTypeInt64)
59     .value("kNumberTypeUInt8", DataType::kNumberTypeUInt8)
60     .value("kNumberTypeUInt16", DataType::kNumberTypeUInt16)
61     .value("kNumberTypeUInt32", DataType::kNumberTypeUInt32)
62     .value("kNumberTypeUInt64", DataType::kNumberTypeUInt64)
63     .value("kNumberTypeFloat16", DataType::kNumberTypeFloat16)
64     .value("kNumberTypeBFloat16", DataType::kNumberTypeBFloat16)
65     .value("kNumberTypeFloat32", DataType::kNumberTypeFloat32)
66     .value("kNumberTypeFloat64", DataType::kNumberTypeFloat64)
67     .value("kInvalidType", DataType::kInvalidType);
68 
69   (void)py::enum_<Format>(m, "Format")
70     .value("DEFAULT_FORMAT", Format::DEFAULT_FORMAT)
71     .value("NCHW", Format::NCHW)
72     .value("NHWC", Format::NHWC)
73     .value("NHWC4", Format::NHWC4)
74     .value("HWKC", Format::HWKC)
75     .value("HWCK", Format::HWCK)
76     .value("KCHW", Format::KCHW)
77     .value("CKHW", Format::CKHW)
78     .value("KHWC", Format::KHWC)
79     .value("CHWK", Format::CHWK)
80     .value("HW", Format::HW)
81     .value("HW4", Format::HW4)
82     .value("NC", Format::NC)
83     .value("NC4", Format::NC4)
84     .value("NC4HW4", Format::NC4HW4)
85     .value("NCDHW", Format::NCDHW)
86     .value("NWC", Format::NWC)
87     .value("NCW", Format::NCW)
88     .value("NDHWC", Format::NDHWC)
89     .value("NC8HW8", Format::NC8HW8);
90 
91   (void)py::class_<MSTensor::Impl, std::shared_ptr<MSTensor::Impl>>(m, "TensorImpl_");
92   (void)py::class_<MSTensor, std::shared_ptr<MSTensor>>(m, "TensorBind")
93     .def(py::init<>())
94     .def("set_tensor_name", [](MSTensor &tensor, const std::string &name) { tensor.SetTensorName(name); })
95     .def("get_tensor_name", &MSTensor::Name)
96     .def("set_data_type", &MSTensor::SetDataType)
97     .def("get_data_type", &MSTensor::DataType)
98     .def("set_shape", &MSTensor::SetShape)
99     .def("get_shape", &MSTensor::Shape)
100     .def("set_format", &MSTensor::SetFormat)
101     .def("get_format", &MSTensor::format)
102     .def("get_element_num", &MSTensor::ElementNum)
103     .def("get_data_size", &MSTensor::DataSize)
104     .def("set_data", &MSTensor::SetData)
105     .def("get_data", &MSTensor::MutableData)
106     .def("is_null", [](const MSTensorPtr &tensor) { return tensor == nullptr; })
107     .def("get_tensor_device_type",
108          [](const MSTensorPtr &tensor) {
109            std::string device = "None";
110            if (!tensor->GetDevice().empty()) {
111              device = tensor->GetDevice();
112            }
113            return device + ":" + std::to_string(tensor->GetDeviceId());
114          })
115     .def("set_data_from_numpy",
116          [](const MSTensorPtr &tensor, const py::array &input) { return SetTensorNumpyData(tensor, input); })
117     .def("get_data_to_numpy", [](const MSTensorPtr &tensor) -> py::array {
118       if (tensor == nullptr) {
119         MS_LOG(ERROR) << "Tensor object cannot be nullptr";
120         return py::array();
121       }
122       auto shape = tensor->Shape();
123       if (std::any_of(shape.begin(), shape.end(), [](auto item) { return item <= 0; })) {
124         MS_LOG(ERROR) << "Tensor shape " << shape << " is invalid";
125         return py::array();
126       }
127       auto elem_num = tensor->ElementNum();
128       if (elem_num <= 0) {
129         MS_LOG(ERROR) << "Tensor element num " << elem_num << " cannot <= 0";
130         return py::array();
131       }
132       auto info = GetPyBufferInfo(tensor);
133       py::object self = py::cast(tensor->impl());
134       return py::array(py::dtype(info), info.shape, info.strides, info.ptr, self);
135     });
136 }
137 
create_tensor(DataType data_type,const std::vector<int64_t> & shape,const std::string & device_type,int device_id)138 MSTensorPtr create_tensor(DataType data_type, const std::vector<int64_t> &shape, const std::string &device_type,
139                           int device_id) {
140   auto tensor = mindspore::MSTensor::CreateTensor("", data_type, shape, nullptr, 0, device_type, device_id);
141   if (tensor == nullptr) {
142     MS_LOG(ERROR) << "create tensor failed.";
143     return nullptr;
144   }
145   mindspore::Format data_format = NCHW;
146   tensor->SetFormat(data_format);
147   return MSTensorPtr(tensor);
148 }
149 
create_tensor_by_tensor(const MSTensor & tensor,const std::string & device_type,int device_id)150 MSTensorPtr create_tensor_by_tensor(const MSTensor &tensor, const std::string &device_type, int device_id) {
151   auto new_tensor = mindspore::MSTensor::CreateTensor("", tensor, device_type, device_id);
152   if (new_tensor == nullptr) {
153     MS_LOG(ERROR) << "create tensor failed.";
154     return nullptr;
155   }
156   new_tensor->SetFormat(tensor.format());
157   return MSTensorPtr(new_tensor);
158 }
159 
create_tensor_by_numpy(const py::array & input,const std::string & device_type,int32_t device_id)160 MSTensorPtr create_tensor_by_numpy(const py::array &input, const std::string &device_type, int32_t device_id) {
161   // Check format.
162   if (!IsCContiguous(input)) {
163     MS_LOG(ERROR) << "Numpy array is not C Contiguous";
164     return nullptr;
165   }
166   auto py_buffer_info = input.request();
167   auto py_data_type = TensorNumpyImpl::GetDataType(py_buffer_info);
168   auto py_data_size = py_buffer_info.size * py_buffer_info.itemsize;
169   auto py_shape = py_buffer_info.shape;
170   auto data_size = mindspore::CalTensorDataSize(py_shape, py_data_type);
171   if (py_data_size != static_cast<int64_t>(data_size)) {
172     MS_LOG(ERROR) << "Expect data size " << data_size << ", but got " << py_data_size;
173     return nullptr;
174   }
175   auto tensor_impl = std::make_shared<TensorNumpyImpl>("", std::move(py_buffer_info), py_shape);
176   tensor_impl->SetDevice(device_type);
177   tensor_impl->SetDeviceId(device_id);
178   auto numpy_tensor = std::make_shared<MSTensor>(tensor_impl);
179   if (numpy_tensor == nullptr) {
180     MS_LOG(ERROR) << "Create numpy tensor failed.";
181     return nullptr;
182   }
183 #ifdef ENABLE_CLOUD_INFERENCE
184   if (device_type == "ascend") {
185     kernel::AscendAllocatorPlugin::GetInstance().Register();
186     device_id = device_id == -1 ? kernel::AscendAllocatorPlugin::GetInstance().GetCurrentDeviceId() : device_id;
187     auto device_data = kernel::AscendAllocatorPlugin::GetInstance().Malloc(data_size, device_id);
188     if (device_data == nullptr) {
189       MS_LOG(ERROR) << "Malloc device data for numpy tensor failed.";
190       return nullptr;
191     }
192     auto status = kernel::AscendAllocatorPlugin::GetInstance().CopyHostDataToDevice(numpy_tensor->MutableData(),
193                                                                                     device_data, data_size);
194     if (status != kSuccess) {
195       MS_LOG(ERROR) << "tensor has device data, then copy host data to device failed.";
196       return nullptr;
197     }
198     numpy_tensor->SetDeviceData(device_data);
199   }
200 #endif
201   return numpy_tensor;
202 }
203 
GetPyTypeFormat(DataType data_type)204 std::string GetPyTypeFormat(DataType data_type) {
205   switch (data_type) {
206     case DataType::kNumberTypeFloat32:
207       return py::format_descriptor<float>::format();
208     case DataType::kNumberTypeFloat64:
209       return py::format_descriptor<double>::format();
210     case DataType::kNumberTypeUInt8:
211       return py::format_descriptor<uint8_t>::format();
212     case DataType::kNumberTypeUInt16:
213       return py::format_descriptor<uint16_t>::format();
214     case DataType::kNumberTypeUInt32:
215       return py::format_descriptor<uint32_t>::format();
216     case DataType::kNumberTypeUInt64:
217       return py::format_descriptor<uint64_t>::format();
218     case DataType::kNumberTypeInt8:
219       return py::format_descriptor<int8_t>::format();
220     case DataType::kNumberTypeInt16:
221       return py::format_descriptor<int16_t>::format();
222     case DataType::kNumberTypeInt32:
223       return py::format_descriptor<int32_t>::format();
224     case DataType::kNumberTypeInt64:
225       return py::format_descriptor<int64_t>::format();
226     case DataType::kNumberTypeBool:
227       return py::format_descriptor<bool>::format();
228     case DataType::kObjectTypeString:
229       return py::format_descriptor<uint8_t>::format();
230     case DataType::kNumberTypeFloat16:
231       return "e";
232     case DataType::kNumberTypeBFloat16:
233       return "e";
234     default:
235       MS_LOG(ERROR) << "Unsupported DataType " << static_cast<int>(data_type) << ".";
236       return "";
237   }
238 }
239 
SetTensorNumpyData(const MSTensorPtr & tensor_ptr,const py::array & input)240 bool SetTensorNumpyData(const MSTensorPtr &tensor_ptr, const py::array &input) {
241   auto &tensor = *tensor_ptr;
242   // Check format.
243   if (!IsCContiguous(input)) {
244     MS_LOG(ERROR) << "Numpy array is not C Contiguous";
245     return false;
246   }
247   auto py_buffer_info = input.request();
248   auto py_data_type = TensorNumpyImpl::GetDataType(py_buffer_info);
249   if (py_data_type != tensor.DataType()) {
250     MS_LOG(ERROR) << "Expect data type " << static_cast<int>(tensor.DataType()) << ", but got "
251                   << static_cast<int>(py_data_type);
252     return false;
253   }
254   auto py_data_size = py_buffer_info.size * py_buffer_info.itemsize;
255   if (py_data_size != static_cast<int64_t>(tensor.DataSize())) {
256     MS_LOG(ERROR) << "Expect data size " << tensor.DataSize() << ", but got " << py_data_size << ", expected shape "
257                   << tensor.Shape() << ", got shape " << py_buffer_info.shape;
258     return false;
259   }
260 #ifdef ENABLE_CLOUD_INFERENCE
261   if (tensor.GetDeviceData() != nullptr) {
262     MS_LOG(INFO) << "device tensor data ptr is not nullptr, need copy host data to device data.";
263     auto status = kernel::AscendAllocatorPlugin::GetInstance().CopyHostDataToDevice(
264       py_buffer_info.ptr, tensor.GetDeviceData(), tensor.DataSize());
265     if (status != kSuccess) {
266       MS_LOG(ERROR) << "tensor has device data, then copy host data to device failed.";
267       return false;
268     }
269     return true;
270   }
271 #endif
272   auto tensor_impl = std::make_shared<TensorNumpyImpl>(tensor.Name(), std::move(py_buffer_info), tensor.Shape());
273   MS_CHECK_TRUE_RET(tensor_impl != nullptr, false);
274   tensor = MSTensor(tensor_impl);
275   return true;
276 }
277 
GetPyBufferInfo(const MSTensorPtr & tensor)278 py::buffer_info GetPyBufferInfo(const MSTensorPtr &tensor) {
279   ssize_t item_size = static_cast<ssize_t>(tensor->DataSize()) / tensor->ElementNum();
280   std::string format = GetPyTypeFormat(tensor->DataType());
281   auto lite_shape = tensor->Shape();
282   ssize_t ndim = lite_shape.size();
283   std::vector<ssize_t> shape(lite_shape.begin(), lite_shape.end());
284   std::vector<ssize_t> strides(ndim);
285   ssize_t element_num = 1;
286   for (int i = ndim - 1; i >= 0; i--) {
287     strides[i] = element_num * item_size;
288     element_num *= shape[i];
289   }
290 #ifdef ENABLE_CLOUD_INFERENCE
291   auto device_data = tensor->GetDeviceData();
292   if (device_data != nullptr) {
293     MS_LOG(INFO) << "need copy host data to device.";
294     // device data is not nullptr, data in device, need copy device data to host.
295     auto status = kernel::AscendAllocatorPlugin::GetInstance().CopyDeviceDataToHost(
296       device_data, tensor->MutableData(), tensor->DataSize(), tensor->GetDeviceId());
297     if (status != kSuccess) {
298       MS_LOG(ERROR) << "tensor has device data, then copy device data to host failed.";
299       return py::buffer_info{nullptr, 0, format, 0, {}, {}};
300     }
301   }
302 #endif
303   return py::buffer_info{tensor->MutableData(), item_size, format, ndim, shape, strides};
304 }
305 }  // namespace mindspore::lite
306