/** * Copyright 2020 Huawei Technologies Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ir/tensor.h" #include #include #include #include #include #include "device/device_address.h" #include "pybind_api/api_register.h" #include "pybind_api/export_flags.h" #include "pipeline/static_analysis/abstract_value.h" namespace mindspore { namespace tensor { void DataBuf2Contiguous(const py::array &src, py::array *const dest) { if (dest == nullptr) { MS_LOG(EXCEPTION) << "Failed to copy data to a contiguous buffer as dest is nullptr!"; } Py_buffer pybuf_src; if (PyObject_GetBuffer(src.ptr(), &pybuf_src, PyBUF_ANY_CONTIGUOUS)) { MS_LOG(EXCEPTION) << "Failed to get buffer info from the src!"; } if (!PyBuffer_IsContiguous(&pybuf_src, 'C')) { if (PyBuffer_ToContiguous(dest->request(true).ptr, &pybuf_src, pybuf_src.len, 'C')) { MS_LOG(EXCEPTION) << "Can't copy numpy.ndarray to a contiguous buffer."; } } else { *dest = src; } PyBuffer_Release(&pybuf_src); } Tensor::Tensor(const TypePtr &type_ptr, const py::tuple &shape) { TypeId data_type = TypeId::kTypeUnknown; if (type_ptr != nullptr) { data_type = type_ptr->type_id(); } data_type_ = data_type; shape_.resize(shape.size()); for (size_t i = 0; i < shape.size(); ++i) { shape_[i] = py::int_(shape[i]); } init(data_type_, shape_, &data_); } Tensor::Tensor(TypeId data_type, const std::vector &shape) { init(data_type, shape, &data_); } Tensor::Tensor(const py::array &input, const TypePtr &data_type) { init(input, data_type); } Tensor::Tensor(const py::list &input, const TypePtr &data_type) { init(py::array(input), data_type); } Tensor::Tensor(const py::tuple &input, const TypePtr &data_type) { init(py::array(input), data_type); } Tensor::Tensor(const py::float_ &input, const TypePtr &data_type) { init(py::array(input), data_type); } Tensor::Tensor(const py::int_ &input, const TypePtr &data_type) { init(py::array(input), data_type); } Tensor::Tensor(const Tensor &tensor, const TypePtr &data_type) : MetaTensor(tensor), device_address_(tensor.device_address_) { init(tensor.data_, data_type); dirty_ = tensor.is_dirty(); id_ = tensor.id(); } Tensor &Tensor::operator=(const Tensor &tensor) { if (this != &tensor) { MetaTensor::operator=(tensor); dirty_ = tensor.is_dirty(); device_address_ = tensor.device_address(); data_ = tensor.data_; id_ = tensor.id(); } return *this; } bool Tensor::operator==(const Tensor &tensor) const { return (MetaTensor::operator==(tensor) && data_ == tensor.data_); } bool Tensor::ValueEqual(const Tensor &other) const { auto equal = [&other, this]() -> bool { auto np = py::module::import("numpy"); auto equal = np.attr("equal")(data_, other.data_); auto all_equal = np.attr("all")(equal); return all_equal.cast(); }; return (MetaTensor::operator==(other) && (data_.is(other.data_) || equal())); } py::tuple Tensor::GetPyTupleShape() const { std::vector shape = this->shape(); py::tuple dims(shape.size()); for (size_t i = 0; i < dims.size(); ++i) { dims[i] = py::int_(shape[i]); } return dims; } int Tensor::DataDim() const { return static_cast(data_.ndim()); } int Tensor::DataSize() const { return static_cast(data_.size()); } py::array Tensor::data() const { return data_; } int Tensor::data_type_c() const { return static_cast(data_type_); } std::vector Tensor::shape_c(void) const { return shape(); } void *Tensor::data_c(bool writable) { // operand of bit operation should be unsigned int. unsigned int flags = ((unsigned int)data_.flags()) & pybind11::detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_; bool is_c_contiguous = (flags != 0) ? true : false; if (!is_c_contiguous) { py::array data_c; init(data_type_, shape_, &data_c); DataBuf2Contiguous(data_, &data_c); data_ = data_c; } return data_.request(writable).ptr; } TypeId Tensor::GetDataType(const py::buffer_info &buf) const { TypeId data_type = TypeId::kTypeUnknown; if (buf.format.compare("e") == 0) { data_type = TypeId::kNumberTypeFloat16; } else if (buf.format.compare("f") == 0) { data_type = TypeId::kNumberTypeFloat32; } else if (buf.format.compare("d") == 0) { data_type = TypeId::kNumberTypeFloat64; } else if (buf.format.compare("B") == 0) { data_type = TypeId::kNumberTypeUInt8; } else if (buf.format.compare("H") == 0) { data_type = TypeId::kNumberTypeUInt16; } else if (buf.format.compare("I") == 0) { data_type = TypeId::kNumberTypeUInt32; } else if (buf.format.compare("L") == 0 || buf.format.compare("Q") == 0) { data_type = TypeId::kNumberTypeUInt64; } else if (buf.format.compare("b") == 0) { data_type = TypeId::kNumberTypeInt8; } else if (buf.format.compare("h") == 0) { data_type = TypeId::kNumberTypeInt16; } else if (buf.format.compare("i") == 0) { data_type = TypeId::kNumberTypeInt32; } else if (buf.format.compare("l") == 0 || buf.format.compare("q") == 0) { data_type = TypeId::kNumberTypeInt64; } else if (buf.format.compare("?") == 0) { data_type = TypeId::kNumberTypeBool; } else { MS_LOG(WARNING) << "Get unsupported DataType " << buf.format << "."; } return data_type; } void Tensor::init(const py::array &input, const TypePtr &type_ptr) { TypeId data_type = TypeId::kTypeUnknown; if (type_ptr != nullptr) { data_type = type_ptr->type_id(); } init(input, data_type); } void Tensor::init(const py::array &input, const TypeId &data_type) { py::buffer_info buf = input.request(); data_type_ = GetDataType(buf); if (TypeId::kTypeUnknown == data_type && TypeId::kTypeUnknown == data_type_) { MS_LOG(EXCEPTION) << "Unsupported tensor type!"; } std::vector tm = buf.shape; size_t len = tm.size(); std::vector dims(len); for (size_t i = 0; i < len; ++i) { dims[i] = static_cast(tm[i]); } (void)set_shape(dims); if (TypeId::kTypeUnknown != data_type && TypeId::kTypeUnknown != data_type_ && data_type_ != data_type) { // If user defined data type is not same as GetDataType from the data bool success = convert_data(input, data_type_, &data_, data_type); if (success) { data_type_ = data_type; } else { data_type_ = TypeId::kTypeUnknown; MS_LOG(EXCEPTION) << "Convert data from " << data_type_ << " to " << data_type << " failed!"; } } else { data_ = input; } dirty_ = true; id_ = std::to_string((uintptr_t)(this)); } void Tensor::init(TypeId data_type, const std::vector &shape, py::array *const data) { data_type_ = data_type; shape_ = shape; switch (data_type) { case kNumberTypeBool: *data = py::array_t(shape); break; case kNumberTypeInt8: *data = py::array_t(shape); break; case kNumberTypeInt16: *data = py::array_t(shape); break; case kNumberTypeInt32: *data = py::array_t(shape); break; case kNumberTypeInt64: *data = py::array_t(shape); break; case kNumberTypeUInt8: *data = py::array_t(shape); break; case kNumberTypeUInt16: *data = py::array_t(shape); break; case kNumberTypeUInt32: *data = py::array_t(shape); break; case kNumberTypeUInt64: *data = py::array_t(shape); break; case kNumberTypeFloat16: *data = py::array_t(shape); break; case kNumberTypeFloat32: *data = py::array_t(shape); break; case kNumberTypeFloat64: *data = py::array_t(shape); break; default: MS_LOG(EXCEPTION) << "Cannot construct Tensor because of unsupported data type: " << data_type << "."; break; } id_ = std::to_string((uintptr_t)(this)); } TypePtr Tensor::SetDtype(const TypePtr type_ptr) { MS_EXCEPTION_IF_NULL(type_ptr); (void)set_data_type(type_ptr->type_id()); return type_ptr; } TypeId Tensor::set_data_type(const TypeId data_type) { if (data_.size() > 0 && data_type_ != data_type) { bool success = convert_data(data_, data_type_, &data_, data_type); if (success) { data_type_ = data_type; } else { MS_LOG(EXCEPTION) << "Convert data from " << data_type_ << " to " << data_type << " failed!"; } } else if (data_.size() == 0) { data_type_ = data_type; } return data_type_; } bool Tensor::is_init() { return init_flag_; } void Tensor::set_init_flag(bool flag) { init_flag_ = flag; } bool Tensor::convert_data(const py::array &in, const TypeId in_data_type, py::array *const out, const TypeId out_data_type) { if (out == nullptr) { return false; } bool result = true; if (TypeId::kTypeUnknown == in_data_type || TypeId::kTypeUnknown == out_data_type) { result = false; } else if (in_data_type == out_data_type) { *out = in; } else if (TypeId::kNumberTypeFloat64 == out_data_type) { *out = in.attr("astype").cast()("float64").cast(); } else if (TypeId::kNumberTypeFloat32 == out_data_type) { *out = in.attr("astype").cast()("float32").cast(); } else if (TypeId::kNumberTypeFloat16 == out_data_type) { *out = in.attr("astype").cast()("float16").cast(); } else if (TypeId::kNumberTypeInt64 == out_data_type) { *out = in.attr("astype").cast()("int64").cast(); } else if (TypeId::kNumberTypeInt32 == out_data_type) { *out = in.attr("astype").cast()("int32").cast(); } else if (TypeId::kNumberTypeInt16 == out_data_type) { *out = in.attr("astype").cast()("int16").cast(); } else if (TypeId::kNumberTypeInt8 == out_data_type) { *out = in.attr("astype").cast()("int8").cast(); } else if (TypeId::kNumberTypeUInt8 == out_data_type) { *out = in.attr("astype").cast()("uint8").cast(); } else if (TypeId::kNumberTypeUInt16 == out_data_type) { *out = in.attr("astype").cast()("uint16").cast(); } else if (TypeId::kNumberTypeUInt32 == out_data_type) { *out = in.attr("astype").cast()("uint32").cast(); } else if (TypeId::kNumberTypeUInt64 == out_data_type) { *out = in.attr("astype").cast()("uint64").cast(); } else { data_type_ = TypeId::kTypeUnknown; MS_LOG(EXCEPTION) << "Cannot convert from " << TypeIdLabel(in_data_type) << " to " << TypeIdLabel(out_data_type) << "."; } return result; } abstract::AbstractBasePtr Tensor::ToAbstract() { auto tens = shared_from_base(); auto dtype = tens->Dtype(); if (!IsSubType(dtype, kNumber)) { MS_LOG(EXCEPTION) << "Expect tensor type kNumber but got: " << dtype->ToString() << "."; } auto tensor_shape = tens->shape(); auto abs_tensor = std::make_shared(dtype, tensor_shape); abs_tensor->set_value(shared_from_base()); return abs_tensor; } std::string Tensor::GetShapeAndDataTypeInfo() const { std::ostringstream buf; buf << "Tensor \nshape:[" << shape() << "]" << this->Dtype()->ToString(); return buf.str(); } std::string Tensor::ToString() const { const int small_tensor_size = 30; std::ostringstream buf; buf << "Tensor \nshape:[" << shape() << "]" << this->Dtype()->ToString(); // only print small tensor if (DataSize() < small_tensor_size) { buf << "val:" << std::string(py::str(data())); } return buf.str(); } std::string Tensor::ToStringRepr() const { std::ostringstream buf; auto type_ptr = this->Dtype(); MS_EXCEPTION_IF_NULL(type_ptr); buf << "Tensor shape:[" << shape() << "]" << type_ptr->ToString(); buf << "\nval:" << std::string(py::str(data())); return buf.str(); } py::array Tensor::data_sync() { if (device_address_ != nullptr) { if (!device_address_->SyncDeviceToHost(this->shape(), static_cast(this->data().nbytes()), this->data_type(), this->data_c(true))) { MS_LOG(EXCEPTION) << "SyncDeviceToHost when asnumpy."; } } return data_; } REGISTER_PYBIND_DEFINE(Tensor, ([](const py::module *m) { // dtype should define before Tensor, because Tensor init depend dtype (void)py::class_>(*m, "Tensor") .def(py::init(), py::arg("dtype"), py::arg("shape")) .def(py::init(), py::arg("input"), py::arg("dtype") = nullptr) .def(py::init(), py::arg("input"), py::arg("dtype") = nullptr) .def(py::init(), py::arg("input"), py::arg("dtype") = nullptr) .def(py::init(), py::arg("input"), py::arg("dtype") = nullptr) .def(py::init(), py::arg("input"), py::arg("dtype") = nullptr) .def(py::init(), py::arg("input"), py::arg("dtype") = nullptr) .def_readonly(PYTHON_TENSOR_FLAG, &Tensor::parse_info_) .def("asnumpy", &Tensor::data_sync, R"mydelimiter( Convert tensor to numpy.ndarray. Returns: numpy.ndarray. Examples: >>> data = mindspore.Tensor(np.ones((2, 3))) >>> array = data.asnumpy() >>> array array([[1., 1., 1.], [1., 1., 1.]]) )mydelimiter") .def("size", &Tensor::DataSize, R"mydelimiter( Get tensor's data size. Returns: int, the size of tensor. Examples: >>> data = mindspore.Tensor(np.ones((2, 3))) >>> data.size() 6 )mydelimiter") .def("is_init", &Tensor::is_init, R"mydelimiter( Get tensor init_flag. Returns: bool, whether the tensor init. Examples: >>> data = mindspore.Tensor(np.ones((2, 3))) >>> data.is_init() False )mydelimiter") .def("set_init_flag", &Tensor::set_init_flag, R"mydelimiter( Set tensor init_flag. Examples: >>> data = mindspore.Tensor(np.ones((2, 3))) >>> data.set_init_flag(True) )mydelimiter") .def("dim", &Tensor::DataDim, R"mydelimiter( Get tensor's data dimension. Returns: int, the dimension of tensor. Examples: >>> data = mindspore.Tensor(np.ones((2, 3))) >>> data.dim() 2 )mydelimiter") .def("dtype", &Tensor::Dtype, R"mydelimiter( Get the tensor's data type. Returns: type, the data type of tensor. Examples: >>> data = mindspore.Tensor(np.ones((2, 1), np.int32)) >>> data.dtype() Int32 )mydelimiter") .def("set_dtype", &Tensor::SetDtype, R"mydelimiter( Set the tensor's data type. Arg: dtype (:class:`mindspore.dtype`): The type of output tensor. Examples: >>> data = mindspore.Tensor(np.ones((1, 2), np.float32)) >>> data.set_dtype(mindspore.int32) mindspore.int32 )mydelimiter") .def("shape", &Tensor::GetPyTupleShape, R"mydelimiter( Get the tensor's shape. Returns: tuple[int], the shape of tensor. Examples: >>> data = mindspore.Tensor(np.ones((3, 3))) >>> data.shape() (3, 3) )mydelimiter") .def("__str__", &Tensor::ToString) .def("__repr__", &Tensor::ToStringRepr) .def(py::pickle( [](const Tensor &t) { // __getstate__ /* Return a tuple that fully encodes the state of the object */ return py::make_tuple(t.data()); }, [](const py::tuple &t) { // __setstate__ if (t.size() != 1) { throw std::runtime_error("Invalid state!"); } /* Create a new C++ instance */ Tensor tensor(t[0].cast()); return tensor; })); (void)py::class_>(*m, "MetaTensor") .def(py::init>(), py::arg("dtype"), py::arg("shape")) .def_readonly(PYTHON_META_TENSOR_FLAG, &MetaTensor::parse_info_) .def("dtype", &MetaTensor::Dtype, "Get the MetaTensor's dtype.") .def("shape", &MetaTensor::shape, "Get the MetaTensor's shape."); })); } // namespace tensor } // namespace mindspore