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