/* Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. 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. */ // disable numpy compile error #if defined(_MSC_VER) #include typedef SSIZE_T ssize_t; #endif #include #include #include #include #include "paddle/fluid/eager/accumulation/accumulation_node.h" #include "paddle/fluid/eager/api/all.h" #include "paddle/fluid/eager/api/generated/fluid_generated/dygraph_forward_api.h" #include "paddle/fluid/eager/autograd_meta.h" #include "paddle/fluid/eager/grad_node_info.h" #include "paddle/fluid/eager/hooks.h" #include "paddle/fluid/eager/utils.h" #include "paddle/fluid/framework/convert_utils.h" #include "paddle/fluid/framework/tensor_util.h" #include "paddle/fluid/platform/enforce.h" #include "paddle/fluid/pybind/eager.h" #include "paddle/fluid/pybind/eager_utils.h" #include "paddle/fluid/pybind/exception.h" #include "paddle/fluid/pybind/op_function_common.h" #include "paddle/fluid/pybind/slice_utils.h" #include "paddle/fluid/pybind/uva_utils.h" #include "paddle/phi/api/include/api.h" #include "paddle/phi/api/lib/data_transform.h" #include "paddle/phi/common/data_type.h" #include "paddle/phi/core/compat/convert_utils.h" #include "paddle/phi/core/dense_tensor.h" #include "paddle/phi/core/memory/allocation/allocator.h" #include "paddle/phi/core/memory/memcpy.h" #include "paddle/phi/core/sparse_coo_tensor.h" #include "paddle/phi/core/sparse_csr_tensor.h" #include "paddle/phi/core/visit_type.h" #include "paddle/phi/core/vocab/string_array.h" #include "pybind11/detail/internals.h" #include "pybind11/numpy.h" #include "pybind11/pybind11.h" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #include "paddle/common/ddim.h" #include "paddle/common/flags.h" #include "paddle/fluid/eager/api/generated/eager_generated/backwards/nodes.h" #include "paddle/fluid/eager/api/generated/eager_generated/forwards/dygraph_functions.h" #include "paddle/fluid/framework/python_headers.h" #include "paddle/fluid/pybind/cuda_streams_py.h" #include "paddle/fluid/pybind/tensor_py.h" #include "paddle/fluid/pybind/xpu_streams_py.h" #include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h" #include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h" #include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function_registry.h" #include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h" #include "paddle/phi/core/memory/allocation/mmap_allocator.h" #include "paddle/phi/core/tensor_utils.h" #include "paddle/phi/kernels/funcs/math_function.h" #include "paddle/phi/kernels/funcs/strided_utils.h" #include "paddle/utils/pybind.h" COMMON_DECLARE_bool(set_to_1d); COMMON_DECLARE_bool(use_stride_kernel); using egr::ConvertAllInputsToDistTensor; using egr::InputsContainDistTensor; namespace paddle::pybind { extern void InitTensorWithNumpyValue(TensorObject* self, const pybind11::object& array, const Place& place, bool zero_copy); extern PyTypeObject* p_tensor_type; Py_ssize_t GetSliceIndexFromPyObject(PyObject* obj) { if (PyObject_TypeCheck(obj, p_tensor_type)) { VLOG(6) << "Call GetSliceIndexFromTensor in Eager"; Tensor tensor = CastPyArg2Tensor(obj, 0); PADDLE_ENFORCE_EQ( tensor.has_allocation(), true, common::errors::InvalidArgument( "We can only support initialized tensor in slice, however we got " "uninitialized tensor %s, please check your code.", tensor.name())); return GetSliceIndexFromTensor((*static_cast( CastPyArg2Tensor(obj, 0).impl().get()))); } else { PADDLE_THROW(common::errors::InvalidArgument( "We should only get Tensor or VarBase in this " "method, when you reach this means we got another type index.")); } } namespace { #ifdef PADDLE_WITH_DISTRIBUTE DenseTensor ReshardXToReplicated(phi::distributed::DistTensor* dist_tensor) { if (!dist_tensor->dist_attr().is_replicated()) { phi::distributed::TensorDistAttr dist_attr(dist_tensor->dist_attr()); std::vector dims_mapping(dist_tensor->dims().size(), -1); dist_attr.set_dims_mapping(dims_mapping); dist_attr.clean_partial_status(); // reshard to replicate dist tensor VLOG(4) << "Reshard tensor: " << paddle::experimental::ReshardDebugInfo(*dist_tensor, dist_attr); auto* func = phi::distributed::ChooseProperReshardFunction(*dist_tensor, dist_attr); auto* dev_ctx = phi::DeviceContextPool::Instance().Get(dist_tensor->place()); auto out_tensor = func->Eval(dev_ctx, *dist_tensor, dist_attr); return out_tensor->value(); } else { return dist_tensor->value(); } } #endif } // namespace PyDoc_STRVAR(tensor_method_numpy__doc__, // NOLINT R"DOC(numpy($self, /) -- Returns a numpy array shows the value of current Tensor. Returns: ndarray, The numpy value of current Tensor, dtype is same as current Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([[1.0, 2.0, 3.0], ... [4.0, 5.0, 6.0]]) >>> x.numpy() array([[1., 2., 3.], [4., 5., 6.]], dtype=float32) )DOC"); static PyObject* tensor_method_numpy(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (kwargs) { PyObject* arg = PyDict_GetItemString(kwargs, "force"); if (arg && arg == Py_False) { LOG(WARNING) << "Warning: Currently paddle.Tensor.numpy() only supports " "force conversion i.e. t.detach().cpu().numpy()."; } } auto& api = pybind11::detail::npy_api::get(); if (!self->tensor.impl()) { Py_intptr_t py_dims[phi::DDim::kMaxRank]; // NOLINT Py_intptr_t py_strides[phi::DDim::kMaxRank]; // NOLINT py_dims[0] = 0; py_strides[0] = 0; PyObject* array = api.PyArray_NewFromDescr_( api.PyArray_Type_, api.PyArray_DescrFromType_(pybind11::detail::npy_api::NPY_FLOAT_), 1, py_dims, py_strides, nullptr, pybind11::detail::npy_api::NPY_ARRAY_ALIGNED_ | pybind11::detail::npy_api::NPY_ARRAY_WRITEABLE_, nullptr); return array; } auto tensor_dims = self->tensor.shape(); auto numpy_dtype = TensorDtype2NumpyDtype(self->tensor.type()); auto sizeof_dtype = phi::SizeOf(self->tensor.type()); Py_intptr_t py_dims[phi::DDim::kMaxRank]; // NOLINT Py_intptr_t py_strides[phi::DDim::kMaxRank]; // NOLINT size_t py_rank = tensor_dims.size(); size_t numel = 1; if (self->tensor.is_dense_tensor()) { auto tensor_stride = self->tensor.strides(); for (int i = static_cast(tensor_dims.size()) - 1; i >= 0; --i) { py_dims[i] = static_cast(tensor_dims[i]); py_strides[i] = static_cast(sizeof_dtype * tensor_stride[i]); numel *= py_dims[i]; } } else { for (int i = static_cast(tensor_dims.size()) - 1; i >= 0; --i) { py_dims[i] = static_cast(tensor_dims[i]); py_strides[i] = static_cast(sizeof_dtype * numel); numel *= py_dims[i]; } } if (!self->tensor.impl()->initialized()) { if (tensor_dims.empty()) { py_dims[0] = 0; py_strides[0] = 0; PyObject* array = api.PyArray_NewFromDescr_( api.PyArray_Type_, api.PyArray_DescrFromType_(numpy_dtype), 1, py_dims, py_strides, nullptr, pybind11::detail::npy_api::NPY_ARRAY_ALIGNED_ | pybind11::detail::npy_api::NPY_ARRAY_WRITEABLE_, nullptr); return array; } // NOTE(zhiqiu): numpy will allocate memory automatically // if product of dims is not 0 and data is nullptr. // However, paddle's tensor with empty allocation means // not initialized. It is not consistent if tensor.numpy() // holds memory when tensor's allocation is empty. // so we emplace back a 0 to the dims to make it 0-size tensor. // For example, tensor with shape [2,3] becomes [2,3,0]. auto contains_zero = false; for (size_t i = 0; i < py_rank; ++i) { if (py_dims[i] == 0) { contains_zero = true; break; } } if (!contains_zero) { py_dims[tensor_dims.size()] = 0; py_rank += 1; for (size_t i = 0; i < py_rank; ++i) { py_strides[i] = 0; } } PyObject* array = api.PyArray_NewFromDescr_( api.PyArray_Type_, api.PyArray_DescrFromType_(numpy_dtype), static_cast(py_rank), py_dims, py_strides, nullptr, pybind11::detail::npy_api::NPY_ARRAY_ALIGNED_ | pybind11::detail::npy_api::NPY_ARRAY_WRITEABLE_, nullptr); return array; } DenseTensor cpu_tensor; CPUPlace cpu_place; if (self->tensor.is_cpu() || self->tensor.is_gpu_pinned() || self->tensor.is_xpu_pinned()) { eager_gil_scoped_release guard; CPUPlace place; if (self->tensor.is_selected_rows()) { VLOG(6) << "Getting SelectedRows's numpy value"; auto* selected_rows = static_cast(self->tensor.impl().get()); auto* dense_tensor = static_cast(selected_rows->mutable_value()); cpu_tensor.set_meta(dense_tensor->meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor->Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); // deep copy paddle::memory::Copy(place, cpu_tensor.Holder()->ptr(), place, dense_tensor->Holder()->ptr(), dense_tensor->Holder()->size()); } else if (self->tensor.is_dist_tensor()) { #ifdef PADDLE_WITH_DISTRIBUTE VLOG(6) << "Getting DistTensor's numpy value"; auto* dist_tensor = static_cast(self->tensor.impl().get()); auto dense_tensor = ReshardXToReplicated(dist_tensor); cpu_tensor.set_meta(dense_tensor.meta()); // deep copy auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor.Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); // deep copy paddle::memory::Copy(place, cpu_tensor.Holder()->ptr(), place, dense_tensor.Holder()->ptr(), dense_tensor.Holder()->size()); #else PADDLE_THROW( common::errors::Unavailable("The `numpy()` method of (Dist)Tensor " "is not supported in the current " "PaddlePaddle, please recompile and " "installPaddlePaddle with the option " "of `WITH_DISTRIBUTE=ON`.")); #endif } else { VLOG(6) << "Getting DenseTensor's numpy value"; auto dense_tensor = std::dynamic_pointer_cast(self->tensor.impl()); cpu_tensor.set_meta(dense_tensor->meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor->Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); // deep copy paddle::memory::Copy(place, cpu_tensor.Holder()->ptr(), place, dense_tensor->Holder()->ptr(), dense_tensor->Holder()->size()); } #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) } else if (self->tensor.is_gpu()) { eager_gil_scoped_release guard; #if defined(PADDLE_WITH_CUDA) gpuMemcpyKind kind = cudaMemcpyDeviceToHost; #elif defined(PADDLE_WITH_HIP) gpuMemcpyKind kind = hipMemcpyDeviceToHost; phi::DeviceContextPool::Instance().Get(self->tensor.place())->Wait(); #endif if (self->tensor.is_selected_rows()) { VLOG(6) << "Getting SelectedRows's numpy value"; auto* selected_rows = static_cast(self->tensor.impl().get()); auto* dense_tensor = static_cast(selected_rows->mutable_value()); cpu_tensor.set_meta(dense_tensor->meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor->Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); paddle::platform::GpuMemcpySync(cpu_tensor.Holder()->ptr(), dense_tensor->Holder()->ptr(), dense_tensor->Holder()->size(), kind); } else if (self->tensor.is_dist_tensor()) { #ifdef PADDLE_WITH_DISTRIBUTE VLOG(6) << "Getting DistTensor's numpy value"; auto* dist_tensor = static_cast(self->tensor.impl().get()); auto dense_tensor = ReshardXToReplicated(dist_tensor); cpu_tensor.set_meta(dense_tensor.meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor.Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); paddle::platform::GpuMemcpySync(cpu_tensor.Holder()->ptr(), dense_tensor.Holder()->ptr(), dense_tensor.Holder()->size(), kind); #else PADDLE_THROW( common::errors::Unavailable("The numpy() method of DistTensor " "is not supported in the current " "Paddle, please recompile and " "install Paddle with the option " "of WITH_DISTRIBUTE=ON.")); #endif } else { VLOG(6) << "Getting DenseTensor's numpy value"; auto dense_tensor = std::dynamic_pointer_cast(self->tensor.impl()); cpu_tensor.set_meta(dense_tensor->meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor->Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); paddle::platform::GpuMemcpySync(cpu_tensor.Holder()->ptr(), dense_tensor->Holder()->ptr(), dense_tensor->Holder()->size(), kind); } #endif #if defined(PADDLE_WITH_XPU) } else if (self->tensor.is_xpu()) { CPUPlace place; if (self->tensor.is_selected_rows()) { VLOG(6) << "Getting SelectedRows's numpy value"; auto* selected_rows = static_cast(self->tensor.impl().get()); auto* dense_tensor = static_cast(selected_rows->mutable_value()); cpu_tensor.set_meta(dense_tensor->meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor->Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); paddle::memory::Copy(place, cpu_tensor.Holder()->ptr(), dense_tensor->place(), dense_tensor->Holder()->ptr(), dense_tensor->Holder()->size()); } else if (self->tensor.is_dist_tensor()) { #ifdef PADDLE_WITH_DISTRIBUTE VLOG(6) << "Getting DistTensor's numpy value"; auto* dist_tensor = static_cast(self->tensor.impl().get()); auto dense_tensor = ReshardXToReplicated(dist_tensor); cpu_tensor.set_meta(dense_tensor.meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor.Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); paddle::memory::Copy(place, cpu_tensor.Holder()->ptr(), dense_tensor.place(), dense_tensor.Holder()->ptr(), dense_tensor.Holder()->size()); #else PADDLE_THROW( common::errors::Unavailable("The numpy() method of DistTensor " "is not supported in the current " "Paddle, please recompile and " "install Paddle with the option " "of WITH_DISTRIBUTE=ON.")); #endif } else { VLOG(6) << "Getting DenseTensor's numpy value"; auto dense_tensor = std::dynamic_pointer_cast(self->tensor.impl()); cpu_tensor.set_meta(dense_tensor->meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor->Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); paddle::memory::Copy(place, cpu_tensor.Holder()->ptr(), dense_tensor->place(), dense_tensor->Holder()->ptr(), dense_tensor->Holder()->size()); } #endif #ifdef PADDLE_WITH_CUSTOM_DEVICE } else if (self->tensor.is_custom_device()) { eager_gil_scoped_release guard; phi::DeviceContextPool::Instance().Get(self->tensor.place())->Wait(); if (self->tensor.is_selected_rows()) { VLOG(6) << "Getting SelectedRows's numpy value"; auto* selected_rows = static_cast(self->tensor.impl().get()); auto* dense_tensor = static_cast(selected_rows->mutable_value()); cpu_tensor.set_meta(dense_tensor->meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor->Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); phi::DeviceManager::GetDeviceWithPlace(self->tensor.place()) ->MemoryCopyD2H(cpu_tensor.Holder()->ptr(), dense_tensor->Holder()->ptr(), dense_tensor->Holder()->size()); } else { VLOG(6) << "Getting DenseTensor's numpy value"; auto dense_tensor = std::dynamic_pointer_cast(self->tensor.impl()); // TODO(qili93): temporary for ascend npu performance to be removed along // with npu_identity op Tensor temp_tensor(std::make_shared()); if (dense_tensor->storage_properties_initialized()) { temp_tensor = npu_identity_ad_func(self->tensor, -1); dense_tensor = std::dynamic_pointer_cast(temp_tensor.impl()); } cpu_tensor.set_meta(dense_tensor->meta()); auto tmp_allocation_ptr = memory::Alloc(cpu_place, dense_tensor->Holder()->size()); cpu_tensor.ResetHolder(std::shared_ptr( tmp_allocation_ptr.release(), tmp_allocation_ptr.get_deleter())); phi::DeviceManager::GetDeviceWithPlace(self->tensor.place()) ->MemoryCopyD2H(cpu_tensor.Holder()->ptr(), dense_tensor->Holder()->ptr(), dense_tensor->Holder()->size()); } #endif } else { PADDLE_THROW(common::errors::InvalidArgument( "Tensor.numpy() only support cpu tensor.")); RETURN_PY_NONE } void* array_buffer = cpu_tensor.Holder()->ptr(); size_t array_offset = cpu_tensor.offset(); PyObject* base = ToPyObject(Tensor(std::make_shared(std::move(cpu_tensor)))); uintptr_t ptr = reinterpret_cast(array_buffer) + array_offset; PyObject* array = api.PyArray_NewFromDescr_( api.PyArray_Type_, api.PyArray_DescrFromType_(numpy_dtype), static_cast(py_rank), py_dims, py_strides, reinterpret_cast(ptr), pybind11::detail::npy_api::NPY_ARRAY_ALIGNED_ | pybind11::detail::npy_api::NPY_ARRAY_WRITEABLE_, nullptr); api.PyArray_SetBaseObject_(array, base); return array; EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_numpy_for_string_tensor(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto& api = pybind11::detail::npy_api::get(); if (!self->tensor.impl() || !self->tensor.impl()->initialized()) { VLOG(6) << "The StringTensor is uninitialized. Return the empty string " "numpy array."; Py_intptr_t py_dims[phi::DDim::kMaxRank]; // NOLINT Py_intptr_t py_strides[phi::DDim::kMaxRank]; // NOLINT py_dims[0] = 0; py_strides[0] = 0; PyObject* array = api.PyArray_NewFromDescr_( api.PyArray_Type_, api.PyArray_DescrFromType_(pybind11::detail::npy_api::NPY_UNICODE_), 1, py_dims, py_strides, nullptr, pybind11::detail::npy_api::NPY_ARRAY_ALIGNED_ | pybind11::detail::npy_api::NPY_ARRAY_WRITEABLE_, nullptr); return array; } if (self->tensor.is_cpu()) { VLOG(6) << "Getting StringTensor's numpy value"; auto string_tensor = std::dynamic_pointer_cast(self->tensor.impl()); const auto* st_ptr = string_tensor->data(); auto numel = self->tensor.numel(); auto tensor_dims = self->tensor.shape(); // Get the max unicode length of StringTensor to create numpy unicode // string array. auto* longest_pstring = std::max_element( st_ptr, st_ptr + numel, [](const auto& a, const auto& b) { auto a_unicode_len = phi::strings::GetUnicodeStrLen(a.data(), a.size()); auto b_unicode_len = phi::strings::GetUnicodeStrLen(b.data(), b.size()); return a_unicode_len < b_unicode_len; }); size_t max_unicode_length = phi::strings::GetUnicodeStrLen( longest_pstring->data(), longest_pstring->size()); max_unicode_length = (max_unicode_length == 0) ? 1 : max_unicode_length; VLOG(6) << "The max unicode length is " << max_unicode_length; auto sp = std::make_unique(max_unicode_length * numel); // NOLINT auto py_array_data = sp.get(); memset(py_array_data, 0, max_unicode_length * numel * sizeof(uint32_t)); for (int64_t i = 0; i < numel; ++i) { auto curr_unicode_len = phi::strings::GetUnicodeStrLen(st_ptr[i].data(), st_ptr[i].size()); phi::strings::GetUnicodeStr(st_ptr[i].data(), py_array_data + i * max_unicode_length, curr_unicode_len); } py::array array(py::dtype("U" + std::to_string(max_unicode_length)), tensor_dims, {}, py_array_data); return array.release().ptr(); } else { PADDLE_THROW(common::errors::InvalidArgument( "StringTensor.numpy() only support cpu tensor.")); RETURN_PY_NONE } EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method__is_initialized(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY return ToPyObject(self->tensor.initialized()); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method__is_dense_tensor_hold_allocation( TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { return ToPyObject(false); } if (self->tensor.is_dense_tensor()) { auto dense_tensor_ptr = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject( dense_tensor_ptr->IsInitialized() && ((dense_tensor_ptr->numel() > 0 && dense_tensor_ptr->Holder()->ptr()) || dense_tensor_ptr->numel() == 0)); } else if (self->tensor.is_dist_tensor()) { auto dense_tensor = static_cast(self->tensor.impl().get()) ->value(); return ToPyObject( dense_tensor.IsInitialized() && ((dense_tensor.numel() > 0 && dense_tensor.Holder()->ptr()) || dense_tensor.numel() == 0)); } else { return ToPyObject(false); } EAGER_CATCH_AND_THROW_RETURN_NULL } static void IncreaseTensorReferenceCountUntilCopyComplete(const Tensor& tensor, const Place& place) { auto place_ = phi::is_gpu_place(place) ? place : tensor.place(); auto tracer = egr::Controller::Instance().GetCurrentTracer(); auto gc = tracer->MutableGarbageCollectorIfNotExists(place_); // Note(dev): This is an empty callback, the only way is to "reference" // inner memory Holder, so it will not be destructed until the kernels // launched at current stream of given place is finished, such as // CUDAPinned Mem -> CUDA by cudaMemcpyAsync. auto callback = [tensor, place_]() { VLOG(3) << "Run callback of Tensor:" << tensor.name() << " at place " << place_; }; gc->DirectClearCallback(callback); } static PyObject* tensor_method__copy_to(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto place = CastPyArg2Place(PyTuple_GET_ITEM(args, 0), 0); bool blocking = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 1), 1); Tensor cp_tensor; { eager_gil_scoped_release guard; EagerSetDeviceId(); cp_tensor = self->tensor.copy_to(place, blocking); if (!blocking) { IncreaseTensorReferenceCountUntilCopyComplete(self->tensor, place); } egr::EagerUtils::autograd_meta(&cp_tensor)->SetStopGradient(true); egr::EagerUtils::autograd_meta(&cp_tensor) ->SetPersistable( egr::EagerUtils::autograd_meta(&(self->tensor))->Persistable()); } return ToPyObject(cp_tensor); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_reconstruct_from___doc__, // NOLINT R"DOC(reconstruct_from_($self, other/) -- Reconstruct the self with other Tensor. It is a deep copy of 'self = other'. Returns: None. Examples: .. code-block:: pycon >>> import paddle >>> t1 = paddle.to_tensor([1.0], stop_gradient=False) >>> t2 = paddle.to_tensor([2.0], stop_gradient=True) >>> t1.reconstruct_from_(t2) >>> print(t1) Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True, [2.]) )DOC"); static PyObject* tensor_method_reconstruct_from_(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor src_tensor = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0); std::string orig_name = self->tensor.name(); VLOG(6) << "Start Reconstructing Tensor from" << src_tensor.name() << " to " << orig_name; self->tensor = src_tensor; // Recover source name self->tensor.set_name(orig_name); VLOG(6) << "Finished Reconstructing Tensor from" << src_tensor.name() << " to " << self->tensor.name(); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_copy_(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PyObject* other_tensor = nullptr; bool blocking = true; bool non_blocking = false; static char* kwlist[] = {const_cast("other"), const_cast("blocking"), const_cast("non_blocking"), nullptr}; bool flag = PyArg_ParseTupleAndKeywords( args, kwargs, "|Obb", kwlist, &other_tensor, &blocking, &non_blocking); blocking = !blocking || non_blocking ? false : true; PADDLE_ENFORCE_EQ(flag, true, common::errors::PreconditionNotMet( "Could not parse args and kwargs successfully, " "please check your input first and make " "sure you are on the right way. " "The expected arguments as follow: (" "other, blocking, non_blocking)")); Tensor& src_tensor = CastPyArg2Tensor(other_tensor, 0); const phi::distributed::ProcessMesh* mesh = nullptr; if (InputsContainDistTensor(&mesh, src_tensor, self->tensor)) { ConvertAllInputsToDistTensor(mesh, src_tensor, self->tensor); } VLOG(6) << "Start Copy Tensor " << src_tensor.name() << " to " << self->tensor.name(); if (!self->tensor.initialized()) { eager_gil_scoped_release guard; EagerSetDeviceId(); egr::EagerUtils::autograd_meta(&(self->tensor)) ->SetStopGradient( egr::EagerUtils::autograd_meta(&(src_tensor))->StopGradient()); egr::EagerUtils::autograd_meta(&(self->tensor)) ->SetPersistable( egr::EagerUtils::autograd_meta(&(src_tensor))->Persistable()); if (src_tensor.has_allocation()) { self->tensor.copy_(src_tensor, src_tensor.place(), blocking); } } else { if (src_tensor.has_allocation()) { eager_gil_scoped_release guard; EagerSetDeviceId(); self->tensor.copy_(src_tensor, self->tensor.place(), blocking); } } VLOG(6) << "Finish Copy Tensor " << src_tensor.name() << " to " << self->tensor.name(); return ToPyObject(self->tensor); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method__new_shared_tensor__doc__, // NOLINT R"DOC(_new_shared_tensor($self, retain_holder=True, /) -- Returns a new Tensor that shares data with the original Tensor. This method creates a new Tensor object that shares the underlying data storage with the original Tensor. The behavior depends on the `retain_holder` parameter. Notes: - The original Tensor's autograd metadata (including gradients and backward propagation information) is also shared between the two Tensors. Args: retain_holder (bool, optional): Controls whether to share the data holder. - If True (default): The new Tensor shares the exact same underlying data allocation with the original Tensor. Changes to one will affect the other. Additionally, both Tensors share the same autograd metadata. - If False: Creates a new Tensor with the same metadata but with an empty data allocation. The autograd metadata is still shared. Returns: Tensor: A new Tensor object that shares data and autograd metadata with the original Tensor. Raises: ValueError: If the original Tensor has not been initialized. Examples: >>> # doctest: +REQUIRES(env:GPU) >>> import paddle >>> x = paddle.to_tensor([1, 2, 3], stop_gradient=False) >>> y = x._new_shared_tensor() # Shares data and autograd metadata with x >>> y[0] = 10 >>> print(x) # x is also modified Tensor(shape=[3], dtype=int64, place=Place(gpu:0), stop_gradient=True, [10, 2 , 3 ]) >>> z = x._new_shared_tensor(retain_holder=False) # Creates a new Tensor >>> print(z) # z is an empty Tensor with the same metadata as x Tensor(Not initialized) >> x.stop_gradient = False >> w = paddle.to_tensor([1,2,3]) >> w.stop_gradient = False >> (x + w).sum().backward() >> x.grad Tensor(shape=[3], dtype=int64, place=Place(gpu:0), stop_gradient=False, [1, 1, 1]) >> z.grad Tensor(shape=[3], dtype=int64, place=Place(gpu:0), stop_gradient=False, [1, 1, 1]) )DOC"); static PyObject* tensor_method__new_shared_tensor(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE_EQ( self->tensor.defined(), true, common::errors::InvalidArgument("Tensor %s has not been initialized!", self->tensor.name())); bool retain_holder = true; int nargs = args ? static_cast(PyTuple_Size(args)) : 0; int remaining_kwargs = kwargs ? static_cast(PyDict_Size(kwargs)) : 0; PyObject* retain_holder_obj = GetItemFromArgsOrKWArgs( args, 0, kwargs, {"retain_holder"}, nargs, &remaining_kwargs); retain_holder = CastPyArg2Boolean(retain_holder_obj, "_new_shared_tensor", 0, true); PyObject* obj = p_tensor_type->tp_alloc(p_tensor_type, 0); if (obj) { auto v = reinterpret_cast(obj); new (&(v->tensor)) Tensor(); if (retain_holder) { v->tensor.set_impl(self->tensor.impl()); } else { auto* dense_tensor = dynamic_cast(self->tensor.impl().get()); if (dense_tensor != nullptr && dense_tensor->Holder() != nullptr) { auto tmp = std::make_shared( std::make_shared( nullptr, 0, dense_tensor->Holder()->place()), dense_tensor->meta()); v->tensor.set_impl(tmp); } } v->tensor.set_name(self->tensor.name()); v->tensor.set_autograd_meta(self->tensor.mutable_autograd_meta()); } else { PADDLE_THROW( common::errors::Fatal("tp_alloc return null, can not new a PyObject.")); } return obj; EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_clone__doc__, // NOLINT R"DOC(clone($self, /) -- Returns a new Tensor, which is clone of origin Tensor, and it remains in the current graph. It will always have a Tensor copy. In addition, the cloned Tensor provides gradient propagation. Returns: Tensor, The cloned Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor(1.0, stop_gradient=False) >>> clone_x = x.clone() >>> clone_x.retain_grads() >>> y = clone_x**2 >>> y.backward() >>> print(clone_x.stop_gradient) False >>> print(clone_x.grad) Tensor(shape=[], dtype=float32, place=Place(cpu), stop_gradient=False, 2.) >>> print(x.stop_gradient) False >>> print(x.grad) Tensor(shape=[], dtype=float32, place=Place(cpu), stop_gradient=False, 2.) >>> x = paddle.to_tensor(1.0) >>> clone_x = x.clone() >>> clone_x.stop_gradient = False >>> z = clone_x**3 >>> z.backward() >>> print(clone_x.stop_gradient) False >>> print(clone_x.grad) Tensor(shape=[], dtype=float32, place=Place(cpu), stop_gradient=False, 3.) >>> print(x.stop_gradient) True >>> print(x.grad) None )DOC"); static PyObject* tensor_method_clone(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor out; { eager_gil_scoped_release guard; EagerSetDeviceId(); PADDLE_ENFORCE_EQ( self->tensor.has_allocation(), true, common::errors::InvalidArgument( "We can only support initialized tensor in clone, however we got " "uninitialized tensor %s, please check your code.", self->tensor.name())); SetPythonStack(); out = assign_ad_func(self->tensor); } return ToPyObject(out); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_retain_grads__doc__, R"DOC(retain_grads($self, /) -- Enables this Tensor to have their grad populated during backward(). It is a no-op for leaf tensors. Returns: None. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1.0, 2.0, 3.0]) >>> x.stop_gradient = False >>> y = x + x >>> y.retain_grads() >>> loss = y.sum() >>> loss.backward() >>> print(y.grad) Tensor(shape=[3], dtype=float32, place=Place(cpu), stop_gradient=False, [1., 1., 1.]) >>> x = paddle.to_tensor([1.0, 2.0, 3.0]) >>> x.stop_gradient = False >>> y = x + x >>> y.retain_grads() >>> loss = y.sum() >>> loss.backward() >>> print(y.grad) Tensor(shape=[3], dtype=float32, place=Place(cpu), stop_gradient=False, [1., 1., 1.]) )DOC"); // NOLINT static PyObject* tensor_retain_grads(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (egr::Controller::Instance().HasGrad()) { eager_gil_scoped_release guard; auto meta = egr::EagerUtils::autograd_meta(&(self->tensor)); if (!meta->GetMutableGradNode()) { VLOG(6) << "Make grad node of tensor: " << self->tensor.name() << " become accumulation node"; meta->SetGradNode( std::make_shared(self->tensor)); } egr::egr_utils_api::RetainGradForTensor(self->tensor); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_retain_grad__doc__, R"DOC(retain_grad($self, /) -- Enables this Tensor to have their grad populated during backward(). It is a no-op for leaf tensors. This method is an alias for :code:`retain_grads()`. Returns: None. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1.0, 2.0, 3.0]) >>> x.stop_gradient = False >>> y = x + x >>> y.retain_grad() >>> loss = y.sum() >>> loss.backward() >>> print(y.grad) Tensor(shape=[3], dtype=float32, place=Place(cpu), stop_gradient=False, [1., 1., 1.]) )DOC"); PyDoc_STRVAR(tensor_clear_gradient__doc__, // NOLINT R"DOC(clear_gradient($self, set_to_zero=True, /) -- Only for Tensor that has gradient, normally we use this for Parameters since other temporary Tensor doesn't has gradient. The Gradient of current Tensor will be set to ``0`` elementwise or ``None``. Args: set_to_zero (bool, optional): If set to ``True``, the gradient will be set to ``0`` elementwise, otherwise the gradient will be set to ``None``. Default: ``True``. Returns: None. Examples: .. code-block:: pycon >>> import paddle >>> input = paddle.uniform([10, 2]) >>> linear = paddle.nn.Linear(2, 3) >>> out = linear(input) >>> out.backward() >>> print("Before clear_gradient, linear.weight.grad: {}".format(linear.weight.grad)) >>> # doctest: +SKIP("Random output") Before clear_gradient, linear.weight.grad: Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=False, [[-0.03178465, -0.03178465, -0.03178465], [-0.98546225, -0.98546225, -0.98546225]]) >>> # doctest: -SKIP >>> linear.weight.clear_gradient() >>> print("After clear_gradient, linear.weight.grad: {}".format(linear.weight.grad)) After clear_gradient, linear.weight.grad: Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=False, [[0., 0., 0.], [0., 0., 0.]]) )DOC"); static PyObject* tensor_clear_gradient(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(4) << "ClearGradient " << self->tensor.name(); Py_ssize_t args_num = PyTuple_Size(args); bool set_to_zero = true; if (args_num == (Py_ssize_t)1) { set_to_zero = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 0), 0); } Tensor* grad = nullptr; bool is_leaf = egr::EagerUtils::IsLeafTensor(self->tensor); if (is_leaf) { grad = egr::EagerUtils::mutable_grad(self->tensor); PADDLE_ENFORCE( grad != nullptr, common::errors::Fatal("Detected nullptr grad. " "Please check if you have manually cleared " "the grad inside autograd_meta")); } else { auto meta = egr::EagerUtils::unsafe_autograd_meta(self->tensor); grad = meta->MutableGrad(); } if (grad->impl()) { eager_gil_scoped_release guard; if (grad->is_selected_rows()) { auto selected_rows = std::dynamic_pointer_cast(grad->impl()); if (selected_rows->mutable_value()->IsInitialized()) { selected_rows->mutable_rows()->clear(); selected_rows->mutable_value()->clear(); } } else if (grad->is_dense_tensor() || grad->is_dist_tensor()) { if (grad->initialized()) { phi::DenseTensor* grad_t = nullptr; if (grad->is_dense_tensor()) { grad_t = static_cast(grad->impl().get()); } else { grad_t = static_cast(grad->impl().get()) ->unsafe_mutable_value(); } if (set_to_zero) { EagerSetDeviceId(); auto* dev_ctx = phi::DeviceContextPool::Instance().Get(grad_t->place()); phi::funcs::set_constant(*dev_ctx, grad_t, 0.0); if (is_leaf) { std::static_pointer_cast( egr::EagerUtils::grad_node(self->tensor)) ->SetFakeEmpty(true); } } else { VLOG(4) << "Gradient of " << self->tensor.name() << " is initialized, will be released."; grad_t->MoveMemoryHolder(); } } } } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__zero_grads(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(4) << "ZeroGrads " << self->tensor.name(); if (egr::EagerUtils::IsLeafTensor(self->tensor)) { eager_gil_scoped_release guard; EagerSetDeviceId(); // Add RetainGrad as PostHook to AccumulationNode Tensor* grad = egr::EagerUtils::mutable_grad(self->tensor); PADDLE_ENFORCE( grad != nullptr, common::errors::Fatal("Detected nullptr grad. " "Please check if you have manually cleared " "the grad inside autograd_meta")); if (grad->initialized()) { if (grad->is_dense_tensor() || grad->is_dist_tensor()) { phi::DenseTensor* t = nullptr; if (grad->is_dense_tensor()) { t = static_cast(grad->impl().get()); } else { t = static_cast(grad->impl().get()) ->unsafe_mutable_value(); } auto* dev_ctx = phi::DeviceContextPool::Instance().Get(t->place()); phi::funcs::set_constant(*dev_ctx, t, 0.0); } else { grad->set_impl(paddle::experimental::zeros_like(*(grad)).impl()); } } } else { eager_gil_scoped_release guard; EagerSetDeviceId(); auto meta = egr::EagerUtils::unsafe_autograd_meta(self->tensor); if (meta->MutableGrad()->initialized()) { if (meta->MutableGrad()->is_dense_tensor() || meta->MutableGrad()->is_dist_tensor()) { phi::DenseTensor* t = nullptr; if (meta->MutableGrad()->is_dense_tensor()) { t = static_cast(meta->MutableGrad()->impl().get()); } else { t = static_cast( meta->MutableGrad()->impl().get()) ->unsafe_mutable_value(); } auto* dev_ctx = phi::DeviceContextPool::Instance().Get(t->place()); phi::funcs::set_constant(*dev_ctx, t, 0.0); } else { meta->MutableGrad()->set_impl( paddle::experimental::zeros_like(*(meta->MutableGrad())).impl()); } } } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__to_dist(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY const auto& placements = CastPyArg2VectorOfPlacement(PyTuple_GET_ITEM(args, 0), 0); const auto& mesh = CastPyArg2ProcessMesh(PyTuple_GET_ITEM(args, 1), 1); if (self->tensor.is_dense_tensor()) { const auto& dense_tensor_ptr = std::static_pointer_cast(self->tensor.impl()); auto dist_tensor_ptr = std::make_shared( dense_tensor_ptr, mesh, placements); self->tensor.set_impl(dist_tensor_ptr); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__share_buffer_to(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor* dst_ptr = &(reinterpret_cast(PyTuple_GET_ITEM(args, 0))->tensor); if (!self->tensor.initialized()) { if (self->tensor.numel() == 0) { // Do nothing for 0-size Tensor Py_RETURN_NONE; } else { PADDLE_THROW(common::errors::InvalidArgument( "Tensor %s has not been initialized! please initialize " "src tensor before share_buffer_with to other.", self->tensor.name())); } } if (self->tensor.is_dist_tensor()) { auto* src_tensor = static_cast(self->tensor.impl().get()) ->unsafe_mutable_value(); if (!src_tensor->meta().is_contiguous()) { PADDLE_THROW(common::errors::InvalidArgument( "Tensor %s is not contiguous, cannot call share_buffer_to on it.", self->tensor.name())); } if (!dst_ptr->defined()) { dst_ptr->set_impl(std::make_shared()); } auto dst_tensor = static_cast(dst_ptr->impl().get()) ->unsafe_mutable_value(); dst_tensor->ShareBufferWith(*src_tensor); dst_tensor->ShareDataTypeWith(*src_tensor); } else { auto* src_tensor = static_cast(self->tensor.impl().get()); if (!src_tensor->meta().is_contiguous()) { PADDLE_THROW(common::errors::InvalidArgument( "Tensor %s is not contiguous, cannot call share_buffer_to on it.", self->tensor.name())); } if (!dst_ptr->defined()) { dst_ptr->set_impl(std::make_shared()); } auto dst_tensor = static_cast(dst_ptr->impl().get()); dst_tensor->ShareBufferWith(*src_tensor); dst_tensor->ShareDataTypeWith(*src_tensor); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__unsafe_share_buffer_to(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor* dst_ptr = &(reinterpret_cast(PyTuple_GET_ITEM(args, 0))->tensor); if (!self->tensor.initialized()) { if (self->tensor.numel() == 0) { // Do nothing for 0-size Tensor Py_RETURN_NONE; } else { PADDLE_THROW(common::errors::InvalidArgument( "Tensor %s has not been initialized! please initialize " "src tensor before share_buffer_with to other.", self->tensor.name())); } } if (self->tensor.is_dist_tensor()) { auto* src_tensor = static_cast(self->tensor.impl().get()) ->unsafe_mutable_value(); if (!dst_ptr->defined()) { dst_ptr->set_impl(std::make_shared()); } auto dst_tensor = static_cast(dst_ptr->impl().get()) ->unsafe_mutable_value(); dst_tensor->ShareBufferWith(*src_tensor); dst_tensor->ShareDataTypeWith(*src_tensor); } else { auto* src_tensor = static_cast(self->tensor.impl().get()); if (!dst_ptr->defined()) { dst_ptr->set_impl(std::make_shared()); } auto dst_tensor = static_cast(dst_ptr->impl().get()); dst_tensor->ShareBufferWith(*src_tensor); dst_tensor->ShareDataTypeWith(*src_tensor); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__is_shared_buffer_with(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor* dst_ptr = &(reinterpret_cast(PyTuple_GET_ITEM(args, 0))->tensor); PADDLE_ENFORCE_EQ(self->tensor.has_allocation(), true, common::errors::InvalidArgument( "Tensor %s has not been initialized! please initialize " "src tensor before share_buffer_with to other.", self->tensor.name())); bool res = false; if (!self->tensor.defined() || !dst_ptr->defined()) { return ToPyObject(res); } if (self->tensor.is_dist_tensor()) { auto* self_ptr = static_cast(self->tensor.impl().get()) ->unsafe_mutable_value(); auto dst_tensor = static_cast(dst_ptr->impl().get()) ->unsafe_mutable_value(); res = dst_tensor->IsSharedBufferWith(*self_ptr); return ToPyObject(res); } else { auto* self_ptr = static_cast(self->tensor.impl().get()); auto dst_tensor = static_cast(dst_ptr->impl().get()); res = dst_tensor->IsSharedBufferWith(*self_ptr); return ToPyObject(res); } EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__share_underline_tensor_to(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor* src_ptr = &(reinterpret_cast(PyTuple_GET_ITEM(args, 0))->tensor); if (!self->tensor.has_allocation()) { PADDLE_ENFORCE( self->tensor.is_dist_tensor() && !phi::distributed::IsCurRankInMesh( static_cast( self->tensor.impl().get()) ->process_mesh()), common::errors::InvalidArgument( "Tensor %s either lacks impl_ or holder_, Please initialize " "src tensor before share_buffer_with to other.", self->tensor.name())); } src_ptr->set_impl(self->tensor.impl()); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__is_shared_underline_tensor_with(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor src_tensor = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0); PADDLE_ENFORCE_EQ(self->tensor.has_allocation(), true, common::errors::InvalidArgument( "Tensor %s has not been initialized! please initialize " "src tensor before share_buffer_with to other.", src_tensor.name())); bool res = false; if (!self->tensor.defined() || !src_tensor.defined()) { return ToPyObject(res); } res = (self->tensor.impl().get() == src_tensor.impl().get()); return ToPyObject(res); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_detach__doc__, // NOLINT R"DOC(detach($self, /) -- Returns a new Tensor, detached from the current graph. It will share data with origin Tensor and always doesn't have a Tensor copy. In addition, the detached Tensor doesn't provide gradient propagation. Returns: Tensor, The detached Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1.0], stop_gradient=False) >>> detach_x = x.detach() >>> detach_x[0] = 10.0 >>> print(x) Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False, [10.]) >>> y = x**2 >>> y.backward() >>> print(x.grad) Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=False, [20.]) >>> print(detach_x.grad) # None, 'stop_gradient=True' by default None >>> detach_x.stop_gradient = False # Set stop_gradient to be False, supported auto-grad >>> z = detach_x**3 >>> z.backward() >>> print(x.grad) Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=False, [20.]) >>> print(detach_x.grad) Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=False, [300.]) >>> # Due to sharing of data with origin Tensor, There are some unsafe operations: >>> # y = 2 * x >>> # detach_x[:] = 5.0 >>> # y.backward() >>> # It will raise Error: >>> # one of the variables needed for gradient computation has been modified by an inplace operation. )DOC"); static PyObject* tensor_method_detach(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE_EQ( self->tensor.defined(), true, common::errors::InvalidArgument("Tensor %s has not been initialized!", self->tensor.name())); PyObject* obj = p_tensor_type->tp_alloc(p_tensor_type, 0); if (obj) { auto v = reinterpret_cast(obj); new (&(v->tensor)) Tensor(); v->tensor.set_impl(self->tensor.impl()); v->tensor.set_name(self->tensor.name()); auto autograd_meta_src = egr::EagerUtils::autograd_meta(&(self->tensor)); auto autograd_meta = egr::EagerUtils::autograd_meta(&(v->tensor)); autograd_meta->SetPersistable(autograd_meta_src->Persistable()); } else { PADDLE_THROW( common::errors::Fatal("tp_alloc return null, can not new a PyObject.")); } return obj; EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_detach___doc__, R"DOC(detach_($self, /) -- Detach self from the current graph, and returns self Tensor. In addition, the detached Tensor doesn't provide gradient propagation. Returns: Tensor, The detached Tensor. )DOC"); static PyObject* tensor_method_detach_(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE_EQ( self->tensor.defined(), true, common::errors::InvalidArgument("Tensor %s has not been initialized!", self->tensor.name())); auto autograd_meta = std::make_shared(); autograd_meta->SetPersistable( egr::EagerUtils::autograd_meta(&(self->tensor))->Persistable()); self->tensor.set_autograd_meta(autograd_meta); Py_INCREF(reinterpret_cast(self)); return reinterpret_cast(self); EAGER_CATCH_AND_THROW_RETURN_NULL } // NOLINT PyDoc_STRVAR(tensor_method_get_tensor__doc__, R"DOC(get_tensor($self, /) -- Returns the underline tensor in the origin Tensor. Returns: Underline tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1.0], stop_gradient=False) >>> underline_x = x.get_tensor() >>> print(underline_x) - shape: [1] - layout: NCHW - place: Place(cpu) - dtype: float32 - data: [1] )DOC"); // NOLINT static PyObject* tensor_method_get_underline_tensor(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { // The original `get_tensor` method of Variable will create a empty tensor DenseTensor empty_tensor; return ToPyObject(&empty_tensor); } if (self->tensor.is_dense_tensor()) { auto* tensor = static_cast(self->tensor.impl().get()); VLOG(6) << "tensor: " << tensor->IsInitialized(); return ToPyObject(tensor); } else if (self->tensor.is_dist_tensor()) { #ifdef PADDLE_WITH_DISTRIBUTE auto* tensor = static_cast(self->tensor.impl().get()); VLOG(6) << "dist tensor: " << tensor->defined(); return ToPyObject(tensor); #else PADDLE_THROW(common::errors::Unavailable( "The get_tensor() method of DistTensor is not supported in the " "current Paddle, please recompile and install Paddle " "with the option of `WITH_DISTRIBUTE=ON`.")); #endif } else { RETURN_PY_NONE } EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_set_underline_tensor(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto& value = GetTensorFromArgs("set_tensor", "value", args, 0, false); if (!value.defined()) { PADDLE_THROW( common::errors::Unavailable("The set_tensor() method of DistTensor " "get a non initialized src value")); } else if (value.is_dense_tensor()) { auto* src_tensor = static_cast(value.impl().get()); if (self->tensor.is_dense_tensor()) { auto* dst_tensor = static_cast(self->tensor.impl().get()); if (self->tensor.has_allocation() && self->tensor.initialized() && (!dst_tensor->meta().is_contiguous() || !src_tensor->meta().is_contiguous()) && dst_tensor->place().GetType() == src_tensor->place().GetType()) { VLOG(8) << "set_tensor() method , src or dst tensor is not contiguous "; if (!FLAGS_use_stride_kernel) { PADDLE_THROW(common::errors::Fatal( "FLAGS_use_stride_kernel is closed. Strided kernel " "be called, something wrong has happened!")); } PD_VISIT_ALL_TYPES( src_tensor->dtype(), "StridedTensorCopy", ([&] { phi::StridedTensorCopy( *src_tensor, common::vectorize(dst_tensor->dims()), common::vectorize(dst_tensor->strides()), dst_tensor->offset(), dst_tensor); })); } else { if (!dst_tensor->meta().is_contiguous()) { PADDLE_THROW(common::errors::Fatal( "dst_tensor is not contiguous and src_tensor has different place " "with dst_tensor, so Strided kernel " "can't be called, please change src_tensor'place as same as " "dst_tensor'place or change dst_tensor to be contiguous")); } else if (!src_tensor->meta().is_contiguous()) { VLOG(6) << "src_tensor is not contiguous, so dst_tensor will be not " "contiguous after set_value "; } if (dst_tensor->place().GetType() != phi::AllocationType::UNDEFINED) { framework::TensorCopy(*src_tensor, dst_tensor->place(), dst_tensor); } else if (src_tensor->place().GetType() != phi::AllocationType::UNDEFINED) { framework::TensorCopy(*src_tensor, src_tensor->place(), dst_tensor); } else { PADDLE_THROW(common::errors::Unavailable( "The set_tensor() method of DistTensor get a src value with " "undefined place")); } } } else { PADDLE_THROW(common::errors::Unavailable( "The set_tensor() method of non DenseTensor get a DenseTensor src " "value")); } } else if (value.is_dist_tensor()) { #ifdef PADDLE_WITH_DISTRIBUTE auto* src_tensor = static_cast(value.impl().get()); if (self->tensor.is_dist_tensor()) { auto* dst_tensor = static_cast(self->tensor.impl().get()); if (dst_tensor->place().GetType() != phi::AllocationType::UNDEFINED) { framework::TensorCopy(*(src_tensor->unsafe_mutable_value()), dst_tensor->place(), dst_tensor->unsafe_mutable_value()); } else if (src_tensor->place().GetType() != phi::AllocationType::UNDEFINED) { framework::TensorCopy(*(src_tensor->unsafe_mutable_value()), src_tensor->place(), dst_tensor->unsafe_mutable_value()); } else { PADDLE_THROW(common::errors::Unavailable( "The set_tensor() method of DistTensor get a src value with " "undefined place")); } } else { PADDLE_THROW( common::errors::Unavailable("The set_tensor() method of non " "DistTensor get a DistTensor src value")); } #else PADDLE_THROW(common::errors::Unavailable( "The set_tensor() method of DistTensor is not supported in the " "current Paddle, please recompile and install Paddle " "with the option of WITH_DISTRIBUTE=ON.")); #endif } else { PADDLE_THROW(common::errors::Unavailable( "The set_tensor() method of DistTensor get a non " "DenseTensor/DistTensor src value")); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_get_underline_selected_rows(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { RETURN_PY_NONE } if (self->tensor.is_selected_rows()) { auto* selected_rows = static_cast(self->tensor.impl().get()); return ToPyObject(selected_rows); } else { RETURN_PY_NONE } EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method__get_tensor_from_selected_rows( TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE( self->tensor.is_selected_rows(), common::errors::Fatal("this method is only effective for SelectedRows.")); auto* selected_rows = static_cast(self->tensor.impl().get()); PADDLE_ENFORCE(selected_rows->has_allocation(), common::errors::Fatal("SelectedRows must be has_allocation.")); auto* dense_tensor = static_cast(selected_rows->mutable_value()); VLOG(4) << "dense_tensor: " << dense_tensor->has_allocation(); auto t = Tensor(egr::Controller::Instance().GenerateUniqueName()); t.set_impl(std::make_shared(*dense_tensor)); return ToPyObject(t); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__getitem_dygraph(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY SetPythonStack(); PyObject* _index = PyTuple_GET_ITEM(args, 0); VLOG(4) << "Call new indexing strategy _getitem_dygraph"; PyObject* index_ptr = !PyTuple_Check(_index) ? PyTuple_Pack(1, _index) : _index; DEFINE_PADDLE_SCOPE_GUARD([index_ptr, &_index]() { if (!PyTuple_Check(_index)) { Py_DECREF(index_ptr); VLOG(4) << "Call Py_DECREF"; } }); // Note(0x45f): Using defined() instead of initialized() // to support slice tensor which shape like [0, 0, 0]. PADDLE_ENFORCE_EQ( self->tensor.defined(), true, common::errors::InvalidArgument( "tensor %s has not been initialized, we can only slice initialized " "tensor please init it first with numpy or other tensor.", self->tensor.name())); auto tensor = self->tensor; const int rank = tensor.shape().size(); std::vector slice_starts, slice_ends, slice_strides; std::vector slice_axes, decrease_axis, infer_flags, none_axes; bool has_advanced_index = false; bool use_strided_slice = false; std::vector advanced_index_dim( rank == 0 ? 1 : rank * 2, // special case for zero dim tensor -1); // content is dim, multiply 2 is to avoid all index are None std::vector advanced_index; // content is index tensor // step1: parsing the index and recording them ParseIndex(tensor, index_ptr, &slice_axes, &slice_starts, &slice_ends, &slice_strides, &decrease_axis, &none_axes, &infer_flags, &advanced_index_dim, &advanced_index, &has_advanced_index, &use_strided_slice); // step2: Dealing with basic indexing bool out_is_view = false; auto sub_tensor = getTensorWithBasicIndexing(tensor, &slice_axes, &slice_starts, &slice_ends, &slice_strides, &decrease_axis, &none_axes, &infer_flags, &use_strided_slice, &out_is_view); if (!has_advanced_index) { return ToPyObject(sub_tensor); } // step3: Dealing with advanced indexing std::vector transed_index; std::vector trans_back_dim, trans_dim; int pos_of_new_dim = INT_MAX, rank_of_new_dim = 1; Tensor out; Tensor transed_tensor = dealWithAdvancedIndex(sub_tensor, &advanced_index_dim, &advanced_index, false, &transed_index, &trans_back_dim, &pos_of_new_dim, &rank_of_new_dim, &trans_dim, &out_is_view); const int index_size = PyTuple_GET_SIZE(index_ptr); ApplyGetitem(index_size, pos_of_new_dim, rank_of_new_dim, &transed_index, &tensor, &self->tensor, &sub_tensor, &transed_tensor, &out); return ToPyObject(out); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__getitem_from_offset(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY phi::DenseTensor* ptr = nullptr; DenseTensor tensor_after_reshard; if (self->tensor.is_selected_rows()) { auto* selected_rows = static_cast(self->tensor.impl().get()); ptr = static_cast(selected_rows->mutable_value()); } else if (self->tensor.is_dist_tensor()) { #ifdef PADDLE_WITH_DISTRIBUTE auto* dist_tensor = static_cast(self->tensor.impl().get()); PADDLE_ENFORCE( dist_tensor->initialized(), common::errors::Fatal( "The input dist tensor can't be uninitialized for we don't " "know the correct mesh to be reshard.")); const auto& placements = dist_tensor->placements(); bool need_reshard = false; for (const auto& placement : placements) { if (!placement->is_replicated()) { need_reshard = true; break; } } if (need_reshard) { tensor_after_reshard = ReshardXToReplicated(dist_tensor); ptr = &tensor_after_reshard; } else { ptr = dist_tensor->unsafe_mutable_value(); } #else PADDLE_THROW(common::errors::Unavailable( "The `_getitem_from_offset` method of (Dist)Tensor is not supported " "in the current PaddlePaddle, please recompile and install " "PaddlePaddle " "with the option of `WITH_DISTRIBUTE=ON`.")); #endif } else { ptr = static_cast(self->tensor.impl().get()); } PADDLE_ENFORCE_NOT_NULL(ptr, common::errors::InvalidArgument( "%s is not a DenseTensor.", self->tensor.name())); const auto& tensor = *ptr; PADDLE_ENFORCE_EQ( tensor.IsInitialized(), true, common::errors::InvalidArgument( "Tensor of %s is Empty, please check if it has no data.", self->tensor.name())); const auto& tensor_dims = tensor.dims(); std::vector dims(tensor_dims.size()); std::vector stride = common::vectorize(tensor.strides()); size_t numel = 1; for (int i = tensor_dims.size() - 1; i >= 0; --i) { dims[i] = static_cast(tensor_dims[i]); numel *= dims[i]; } size_t offset = 0; if (PyTuple_Size(args) == 0) { PADDLE_ENFORCE_EQ(numel, 1, common::errors::InvalidArgument( "only one element tensors can be converted to Python " "scalars when no input coordinates")); } else if (PyTuple_Size(args) == 1) { offset = CastPyArg2AttrLong(PyTuple_GET_ITEM(args, 0), 0); PADDLE_ENFORCE_LT( offset, numel, common::errors::InvalidArgument( "index %d is out of bounds for size %d", offset, numel)); } else { PADDLE_ENFORCE_EQ(PyTuple_Size(args), dims.size(), common::errors::InvalidArgument( "incorrect number of indices for Tensor")); for (Py_ssize_t i = 0; i < PyTuple_Size(args); ++i) { size_t index = CastPyArg2AttrLong(PyTuple_GET_ITEM(args, i), i); PADDLE_ENFORCE_LT( index, dims[i], common::errors::InvalidArgument( "index %d is out of bounds for axis %d with size %d", index, i, dims[i])); offset += index * stride[i]; } } #define PD_FOR_EACH_DENSE_TENSOR_DATA_TYPE(_) \ _(bool, DataType::BOOL) \ _(int8_t, DataType::INT8) \ _(uint8_t, DataType::UINT8) \ _(int16_t, DataType::INT16) \ _(uint16_t, DataType::UINT16) \ _(int32_t, DataType::INT32) \ _(uint32_t, DataType::UINT32) \ _(int64_t, DataType::INT64) \ _(uint64_t, DataType::UINT64) \ _(bfloat16, DataType::BFLOAT16) \ _(float16, DataType::FLOAT16) \ _(float, DataType::FLOAT32) \ _(double, DataType::FLOAT64) \ _(complex64, DataType::COMPLEX64) \ _(complex128, DataType::COMPLEX128) #define TENSOR_TO_PY_SCALAR(T, proto_type) \ if (tensor.dtype() == proto_type) { \ auto numpy_dtype = TensorDtype2NumpyDtype(proto_type); \ T b = paddle::pybind::TensorGetElement(tensor, offset); \ Py_intptr_t py_dims[phi::DDim::kMaxRank]; /* NOLINT */ \ Py_intptr_t py_strides[phi::DDim::kMaxRank]; /* NOLINT */ \ auto& api = pybind11::detail::npy_api::get(); \ PyObject* array = api.PyArray_NewFromDescr_( \ api.PyArray_Type_, \ api.PyArray_DescrFromType_(numpy_dtype), \ 0, \ py_dims, \ py_strides, \ nullptr, \ pybind11::detail::npy_api::NPY_ARRAY_ALIGNED_ | \ pybind11::detail::npy_api::NPY_ARRAY_WRITEABLE_, \ nullptr); \ std::memcpy( \ reinterpret_cast(pybind11::detail::array_proxy(array)->data), \ static_cast(&b), \ sizeof(b)); \ return array; \ } PD_FOR_EACH_DENSE_TENSOR_DATA_TYPE(TENSOR_TO_PY_SCALAR); #undef TENSOR_TO_PY_SCALAR PADDLE_THROW(common::errors::Unimplemented("Unsupported tensor data type: %s", tensor.dtype())); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__setitem_dygraph(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY SetPythonStack(); VLOG(4) << "Call new indexing strategy _setitem_dygraph"; PyObject* _index = PyTuple_GET_ITEM(args, 0); PyObject* value_obj = PyTuple_GET_ITEM(args, 1); // NOTE(zhiqiu): PyTuple_Pack increases refcount while PyTuple_New // https://github.com/python/cpython/blob/24b63c695ae0a95b06379eaadace66735abac1e2/Objects/tupleobject.c#L251 PyObject* index_ptr = !PyTuple_Check(_index) ? PyTuple_Pack(1, _index) : _index; DEFINE_PADDLE_SCOPE_GUARD([index_ptr, &_index]() { if (!PyTuple_Check(_index)) { Py_DECREF(index_ptr); VLOG(4) << "Call Py_DECREF"; } }); auto tensor = self->tensor; if (egr::Controller::Instance().HasGrad()) { PADDLE_ENFORCE_EQ( egr::EagerUtils::IsLeafTensor(tensor) && !egr::EagerUtils::autograd_meta(&tensor)->StopGradient(), false, common::errors::InvalidArgument( "Leaf Tensor (%s) that doesn't stop gradient can't use " "inplace strategy.", tensor.name())); } const int rank = tensor.shape().size(); const int size = PyTuple_GET_SIZE(index_ptr); std::vector slice_starts, slice_ends, slice_strides; std::vector slice_axes, decrease_axis, infer_flags, none_axes; bool has_advanced_index = false; bool use_strided_slice = false; std::vector advanced_index_dim( rank == 0 ? 1 : rank * 2, // special case for zero dim tensor -1); // content is dim, multiply 2 is to avoid all index are None std::vector advanced_index; // content is index tensor // step1: parsing the index and recording them if (size != 1 || !PyBool_Check(PyTuple_GetItem(index_ptr, 0))) { // single true uses set_value full_set branch // single false does nothing ParseIndex(tensor, index_ptr, &slice_axes, &slice_starts, &slice_ends, &slice_strides, &decrease_axis, &none_axes, &infer_flags, &advanced_index_dim, &advanced_index, &has_advanced_index, &use_strided_slice); } // step2: Parse values std::vector values; Tensor value_tensor = dealWithValues(tensor, value_obj, &values, has_advanced_index); if (!has_advanced_index) { // use set_value OP if there is no advanced index // Release gil and do tracing py::gil_scoped_release release; // use inplace set_value_ operator if (value_tensor.initialized()) { if (self->tensor.dtype() != value_tensor.dtype()) { if (egr::Controller::Instance().GetAMPLevel() != paddle::imperative::AmpLevel::O0) { paddle::small_vector, egr::kSlotSmallVectorSize> tmps = {{self->tensor}, {value_tensor}}; auto amp_dtype = paddle::imperative::GetAmpDestDtype("set_value", tmps); self->tensor = paddle::imperative::AmpAutoCast( self->tensor.name(), self->tensor, amp_dtype, "set_value"); value_tensor = paddle::imperative::AmpAutoCast( value_tensor.name(), value_tensor, amp_dtype, "set_value"); } if (self->tensor.dtype() != value_tensor.dtype()) { value_tensor = cast_ad_func(value_tensor, self->tensor.dtype()); } } // step3.1: Only basic indexing, use OP set_value. const phi::distributed::ProcessMesh* mesh = nullptr; if (InputsContainDistTensor(&mesh, self->tensor, value_tensor)) { ConvertAllInputsToDistTensor(mesh, self->tensor, value_tensor); } if (size != 1 || PyTuple_GetItem(index_ptr, 0) != Py_False) { // if index is single false, do nothing. self->tensor = set_value_with_tensor__ad_func(self->tensor, value_tensor, slice_starts, slice_ends, slice_strides, slice_axes, decrease_axis, none_axes); } if (PyCheckTensor(value_obj)) { // pass the stop_gradient from value to tensor. // pass stop gradient should be done after CheckInplace in // set_value__dygraph_function. if (!egr::EagerUtils::autograd_meta(&value_tensor)->StopGradient() && egr::EagerUtils::autograd_meta(&self->tensor)->StopGradient()) { egr::EagerUtils::autograd_meta(&self->tensor)->SetStopGradient(false); } } } else { const phi::distributed::ProcessMesh* mesh = nullptr; if (InputsContainDistTensor(&mesh, self->tensor)) { ConvertAllInputsToDistTensor(mesh, self->tensor); } if (size != 1 || PyTuple_GetItem(index_ptr, 0) != Py_False) { // if index is single false, do nothing. self->tensor = set_value__ad_func(self->tensor, slice_starts, slice_ends, slice_strides, slice_axes, decrease_axis, none_axes, {1}, values); } } } else { // step3.2: Case for there are advanced indexing. // 1. get __getitem__ result of basic indexing; // 2. transpose original tensor so that the axis with advanced indexing // will come to the first; // 3. assign values to the sliced result by index_put OP; // 4. transpose back and assign the result to original tensor by set_value // OP. bool out_is_view = false; Tensor sub_tensor = getTensorWithBasicIndexing(tensor, &slice_axes, &slice_starts, &slice_ends, &slice_strides, &decrease_axis, &none_axes, &infer_flags, &use_strided_slice, &out_is_view); std::vector transed_index; std::vector trans_back_dim, trans_dim; int pos_of_new_dim = INT_MAX, rank_of_new_dim = 1; Tensor transed_sub_tensor = dealWithAdvancedIndex(sub_tensor, &advanced_index_dim, &advanced_index, true, &transed_index, &trans_back_dim, &pos_of_new_dim, &rank_of_new_dim, &trans_dim, &out_is_view); // Release gil and do tracing py::gil_scoped_release release; ApplySetitem(trans_dim, pos_of_new_dim, &out_is_view, &transed_index, &tensor, &self->tensor, &sub_tensor, &transed_sub_tensor, &value_tensor, &values); if (out_is_view) { // NOTE(zoooo0820): if out_is_view is true, it is a case of // combined-indexing setitem, i.e. firstly we get a view of // self->tensor, then modified it with inplace api index_put_ For now, // in design of Paddle, the forward result is right. But the backward // edge can not be established because the Base Tensor cannot sense // whether it has been modified by other operations. Following codes are // to add a new node (set_value_with_tensor_grad) to record the backward // edge, with out ad_function which needs to do the forward calculation. egr::AutogradMeta* x_autograd_meta = egr::EagerUtils::nullable_autograd_meta(self->tensor); egr::AutogradMeta* values_autograd_meta = egr::EagerUtils::nullable_autograd_meta(transed_sub_tensor); bool trace_backward = egr::Controller::Instance().HasGrad(); bool require_any_grad = egr::EagerUtils::ComputeRequireGrad( trace_backward, x_autograd_meta, values_autograd_meta); // Node Declaration std::shared_ptr grad_node; // Set grad_node before API Call if (require_any_grad) { Tensor transback_sub_tensor = transpose_ad_func(transed_sub_tensor, trans_back_dim); const auto& values_tmp = (require_any_grad && transback_sub_tensor.is_dense_tensor() && !std::dynamic_pointer_cast( transback_sub_tensor.impl()) ->meta() .is_contiguous()) ? Tensor(std::make_shared( paddle::experimental::Trans2Contiguous( *(std::dynamic_pointer_cast( transback_sub_tensor.impl())))), transback_sub_tensor.mutable_autograd_meta(), transback_sub_tensor.name()) : transback_sub_tensor; if (!x_autograd_meta) { VLOG(3) << "x_autograd_meta is null and requires_any_grad is true"; x_autograd_meta = egr::EagerUtils::autograd_meta(&self->tensor); } grad_node = std::shared_ptr( new SetValueWithTensorGradNode(1, 2)); // NOLINT grad_node->SetAttribute_starts(slice_starts); grad_node->SetAttribute_ends(slice_ends); grad_node->SetAttribute_steps(slice_strides); grad_node->SetAttribute_axes(slice_axes); grad_node->SetAttribute_decrease_axes(decrease_axis); grad_node->SetAttribute_none_axes(none_axes); grad_node->SetTensorWrapper_values(values_tmp); paddle::memory::LogDeviceMemoryStats( egr::Controller::Instance().GetExpectedPlace(), "set_value_with_tensor"); egr::EagerUtils::CheckInplace( self->tensor, x_autograd_meta, require_any_grad); egr::EagerUtils::PassStopGradient(false, x_autograd_meta); // SetGradOutMeta & SetEdges grad_node->SetGradOutMeta(self->tensor, 0); grad_node->SetGradOutMeta(transback_sub_tensor, 1); grad_node->SetGradInMeta(self->tensor, 0); egr::EagerUtils::SetOutRankWithSlot(x_autograd_meta, 0); egr::EagerUtils::SetHistory(x_autograd_meta, grad_node); } } else { self->tensor.set_autograd_meta( transed_sub_tensor.mutable_autograd_meta()); } if (value_tensor.initialized() && PyCheckTensor(value_obj)) { // pass the stop_gradient from value to tensor. // pass stop gradient should be done after CheckInplace in // set_value__dygraph_function. if (!egr::EagerUtils::autograd_meta(&value_tensor)->StopGradient() && egr::EagerUtils::autograd_meta(&self->tensor)->StopGradient()) { egr::EagerUtils::autograd_meta(&self->tensor)->SetStopGradient(false); } } } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_apply(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PyObject* apply_func = PyTuple_GET_ITEM(args, 0); PyTensorHook func = PyTensorHook(apply_func); Tensor out = func(self->tensor); return ToPyObject(out); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_apply_(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PyObject* apply_func = PyTuple_GET_ITEM(args, 0); PyTensorHook func = PyTensorHook(apply_func); Tensor out = func(self->tensor); self->tensor.set_impl(out.impl()); Py_INCREF(self); return reinterpret_cast(self); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_register_grad_hook(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY SetPythonStack(); int64_t hook_id = 0; if (egr::EagerUtils::IsLeafTensor(self->tensor)) { VLOG(6) << "Register hook for leaf tensor: " << self->tensor.name(); auto autograd_meta = egr::EagerUtils::unsafe_autograd_meta(self->tensor); if (autograd_meta && !autograd_meta->StopGradient()) { if (!autograd_meta->GetMutableGradNode()) { VLOG(6) << "Detected nullptr grad_node, Leaf tensor should have had " "grad_node with type: GradNodeAccumulation."; autograd_meta->SetGradNode( std::make_shared(self->tensor)); } } std::shared_ptr grad_node = egr::EagerUtils::grad_node(self->tensor); auto rank_info = egr::EagerUtils::unsafe_autograd_meta(self->tensor)->OutRankInfo(); PyObject* hook_func = PyTuple_GET_ITEM(args, 0); auto accumulation_grad_node = std::dynamic_pointer_cast(grad_node); hook_id = accumulation_grad_node->RegisterGradientHook( rank_info.first, rank_info.second, std::make_shared(hook_func)); } else { VLOG(6) << "Register hook for non leaf tensor: " << self->tensor.name(); std::shared_ptr grad_node = egr::EagerUtils::grad_node(self->tensor); auto rank_info = egr::EagerUtils::unsafe_autograd_meta(self->tensor)->OutRankInfo(); PyObject* hook_func = PyTuple_GET_ITEM(args, 0); hook_id = grad_node->RegisterGradientHook( rank_info.first, rank_info.second, std::make_shared(hook_func)); } return ToPyObject(hook_id); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_remove_grad_hook(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(6) << "Remove the registered hook for tensor: " << self->tensor.name(); std::shared_ptr grad_node = egr::EagerUtils::grad_node(self->tensor); int64_t hook_id = pybind::CastPyArg2AttrLong(PyTuple_GET_ITEM(args, 0), 0); return ToPyObject(grad_node->RemoveGradientHook(hook_id)); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* apply_backward_hook(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(6) << " Apply tensor hook for tensor: " << self->tensor.name(); std::shared_ptr grad_node = egr::EagerUtils::grad_node(self->tensor); PADDLE_ENFORCE_EQ( !egr::EagerUtils::unsafe_autograd_meta(self->tensor)->StopGradient(), true, common::errors::InvalidArgument( "Cannot apply backward hook on a Tensor that stop " "gradient.")); PADDLE_ENFORCE_NE( grad_node.get(), nullptr, common::errors::Fatal("Detected nullptr grad_node," "Leaf tensor should have had grad_node " "with type: GradNodeAccumulation.")); auto accumulation_grad_node = std::dynamic_pointer_cast(grad_node); if (accumulation_grad_node->ReduceHooksRegistered()) { accumulation_grad_node->ApplyReduceHooks(); } RETURN_PY_NONE; EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_inplace_assign(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(6) << "inplace assign for tensor:" << self->tensor.name(); PyObject* other = PyTuple_GET_ITEM(args, 0); PyObject* self_obj = reinterpret_cast(self); ShareTensor(self_obj, other); RETURN_PY_NONE; EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method__register_reduce_hook__doc__, // NOLINT R"DOC(_register_backward_hook($self, hook, /) -- Registers a backward hook for current Tensor. This hook will be called every time the gradient of current Tensor has been fully calculated. There are two differences with `_register_grad_hook`: 1. This backward hook will be executed after the gradient accumulation completed across batches, but the hook registered by `_register_grad_hook` will be executed before the gradient accumulation completed in current batch. 2. This backward hook function should have the following signature: hook() -> None It requires no input and no return value. Args: hook(function): A backward hook to be registered for Tensor.gradient Returns: None )DOC"); static PyObject* tensor_register_reduce_hook(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(4) << "Register reduce hook for tensor: " << self->tensor.name(); std::shared_ptr grad_node = egr::EagerUtils::grad_node(self->tensor); PADDLE_ENFORCE_EQ(egr::EagerUtils::IsLeafTensor(self->tensor), true, common::errors::InvalidArgument( "Only can register backward hook for leaf Tensor.")); PADDLE_ENFORCE_EQ( !egr::EagerUtils::unsafe_autograd_meta(self->tensor)->StopGradient(), true, common::errors::InvalidArgument( "Cannot register backward hook on a Tensor that stop " "gradient.")); PADDLE_ENFORCE(grad_node.get() != nullptr, common::errors::Fatal("Detected nullptr grad_node," "Leaf tensor should have had grad_node " "with type: GradNodeAccumulation.")); PyObject* hook_func = PyTuple_GET_ITEM(args, 0); auto accumulation_grad_node = std::dynamic_pointer_cast(grad_node); accumulation_grad_node->RegisterReduceHook( std::make_shared(hook_func)); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__set_grad_type(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto var_type = pybind::CastPyArg2ProtoType(PyTuple_GET_ITEM(args, 0), 0); auto grad_tensor = egr::EagerUtils::autograd_meta(&self->tensor)->MutableGrad(); if (var_type == framework::proto::VarType::DENSE_TENSOR) { grad_tensor->set_impl(std::make_shared()); } else if (var_type == framework::proto::VarType::SELECTED_ROWS) { grad_tensor->set_impl(std::make_shared()); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__clear(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY self->tensor.reset(); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__clear_dataptr(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY self->tensor.set_impl(nullptr); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__clear_to_zero_allocation(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto* dense_tensor = dynamic_cast(self->tensor.impl().get()); if (dense_tensor != nullptr && dense_tensor->Holder() != nullptr) { DenseTensor tmp(std::make_shared( nullptr, 0, dense_tensor->Holder()->place()), dense_tensor->meta()); dense_tensor->ShareBufferWith(std::move(tmp), /*only_buffer=*/true); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__holder_size(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto* dense_tensor = dynamic_cast(self->tensor.impl().get()); size_t size = 0; if (dense_tensor != nullptr && dense_tensor->Holder() != nullptr) { size = dense_tensor->Holder()->size(); } return PyLong_FromSize_t(size); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__copy_gradient_from(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto src = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0); if (self->tensor.has_allocation()) { PADDLE_ENFORCE_EQ(self->tensor.dtype(), src.dtype(), common::errors::PreconditionNotMet( "Tensor %s has different data type with Tensor %s", self->tensor.name(), src.name())); PADDLE_ENFORCE_EQ(self->tensor.impl()->type_info().id(), src.impl()->type_info().id(), common::errors::PreconditionNotMet( "Tensor %s has different type with Tensor %s, Tensor " "ShareGradientDataWith cannot be performed!", self->tensor.name(), src.name())); } VLOG(6) << "Tensor copy gradient from: " << src.name(); auto* p_grad = egr::EagerUtils::mutable_grad(self->tensor); if (p_grad) { PADDLE_ENFORCE_EQ(src.has_allocation(), true, common::errors::InvalidArgument( "Tensor %s has not been initialized", src.name())); p_grad->set_impl(src.impl()); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__use_gpudnn(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE(self->tensor.defined() && (self->tensor.is_dense_tensor() || self->tensor.is_dist_tensor()), common::errors::Fatal("Function _use_gpudnn is only effective " "for DenseTensor and DistTensor.")); bool use_gpudnn = pybind::CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 0), 0); // Set the same use_gpudnn attribute, return directly phi::DenseTensor* dense_tensor = nullptr; if (self->tensor.is_dist_tensor()) { dense_tensor = static_cast(self->tensor.impl().get()) ->unsafe_mutable_value(); } else { dense_tensor = static_cast(self->tensor.impl().get()); } phi::DenseTensorMeta* dense_tensor_meta = phi::DenseTensorUtils::GetMutableMeta(dense_tensor); if (use_gpudnn == dense_tensor_meta->use_gpudnn) { return ToPyObject(self->tensor); } // Share all other members of Tensor except use_gpudnn phi::DenseTensorMeta target_dense_meta = *dense_tensor_meta; target_dense_meta.use_gpudnn = use_gpudnn; DenseTensor target_dense_tensor; target_dense_tensor.ShareDataWith(*dense_tensor); target_dense_tensor.set_meta(target_dense_meta); // Construct returned tensor Tensor target_tensor(self->tensor.name()); target_tensor.set_autograd_meta(self->tensor.mutable_autograd_meta()); if (self->tensor.is_dist_tensor()) { auto dist_tensor = static_cast(self->tensor.impl().get()); auto target_dist_tensor = std::make_shared( dist_tensor->dims(), dist_tensor->dist_attr()); *(target_dist_tensor->unsafe_mutable_value()) = target_dense_tensor; target_tensor.set_impl(target_dist_tensor); } else { target_tensor.set_impl(std::make_shared(target_dense_tensor)); } VLOG(4) << "Tensor: " << target_tensor.name() << " set use_gpudnn = " << use_gpudnn; return ToPyObject(target_tensor); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_set_vocab(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY using Vocab = phi::Vocab; auto vocab = CastPyArg2Vocab(PyTuple_GET_ITEM(args, 0), 0); auto var_tensor = std::make_shared(); *var_tensor->GetMutable() = vocab; self->tensor.set_impl(var_tensor); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_set_string_list(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY using Strings = phi::Strings; auto strings = CastPyArg2VectorOfString(PyTuple_GET_ITEM(args, 0), 0); auto var_tensor = std::make_shared(); *var_tensor->GetMutable() = strings; self->tensor.set_impl(var_tensor); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_get_map_tensor(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE_EQ( egr::IsVariableCompatTensor(self->tensor), true, common::errors::Fatal( "this method is only effective for VariableCompatTensor")); using Vocab = phi::Vocab; auto* var_tensor = static_cast(self->tensor.impl().get()); return ToPyObject(var_tensor->Get()); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_nnz__doc__, R"DOC(nnz($self, /) -- Note: **This API is only available for SparseCooTensor or SparseCsrTensor.** Returns the total number of non zero elements in input SparseCooTensor/SparseCsrTensor. Returns: int Examples: .. code-block:: pycon >>> import paddle >>> indices = [[0, 1, 2], [1, 2, 0]] >>> values = [1.0, 2.0, 3.0] >>> dense_shape = [3, 3] >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape) >>> coo.nnz() 3 )DOC"); // NOLINT static PyObject* tensor_method_get_non_zero_nums(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE(self->tensor.is_sparse_coo_tensor() || self->tensor.is_sparse_csr_tensor(), common::errors::Fatal("this method is only effective for " "SparseCooTensor or SparseCsrTensor")); if (self->tensor.is_sparse_coo_tensor()) { auto sparse_coo_tensor = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(sparse_coo_tensor->nnz()); } else { auto sparse_csr_tensor = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(sparse_csr_tensor->nnz()); } EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_indices__doc__, R"DOC(indices($self, /) -- Note: **This API is only available for SparseCooTensor.** Returns the indices of non zero elements in input SparseCooTensor. Returns: DenseTensor Examples: .. code-block:: pycon >>> import paddle >>> indices = [[0, 1, 2], [1, 2, 0]] >>> values = [1.0, 2.0, 3.0] >>> dense_shape = [3, 3] >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape) >>> coo.indices() Tensor(shape=[2, 3], dtype=int64, place=Place(gpu:0), stop_gradient=True, [[0, 1, 2], [1, 2, 0]]) )DOC"); // NOLINT static PyObject* tensor_method_get_non_zero_indices(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE(self->tensor.is_sparse_coo_tensor(), common::errors::Fatal( "this method is only effective for SparseCooTensor")); auto sparse_coo_tensor = std::dynamic_pointer_cast(self->tensor.impl()); Tensor tensor( std::make_shared(sparse_coo_tensor->non_zero_indices())); return ToPyObject(tensor); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_values__doc__, R"DOC(values($self, /) -- Note: **This API is only available for SparseCooTensor or SparseCsrTensor.** Returns the values of non zero elements in input SparseCooTensor. Returns: DenseTensor Examples: .. code-block:: pycon >>> import paddle >>> indices = [[0, 1, 2], [1, 2, 0]] >>> values = [1.0, 2.0, 3.0] >>> dense_shape = [3, 3] >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape) >>> coo.values() Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True, [1., 2., 3.]) )DOC"); // NOLINT static PyObject* tensor_method_get_non_zero_elements(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE(self->tensor.is_sparse_coo_tensor() || self->tensor.is_sparse_csr_tensor(), common::errors::Fatal("this method is only effective for " "SparseCooTensor or SparseCsrTensor")); if (self->tensor.is_sparse_coo_tensor()) { auto sparse_coo_tensor = std::dynamic_pointer_cast(self->tensor.impl()); Tensor tensor( std::make_shared(sparse_coo_tensor->non_zero_elements())); return ToPyObject(tensor); } else { auto sparse_csr_tensor = std::dynamic_pointer_cast(self->tensor.impl()); Tensor tensor( std::make_shared(sparse_csr_tensor->non_zero_elements())); return ToPyObject(tensor); } EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_crows__doc__, R"DOC(crows($self, /) -- Note: **This API is only available for SparseCsrTensor.** Returns the compressed row index of non zero elements in input SparseCsrTensor. Returns: DenseTensor Examples: .. code-block:: pycon >>> import paddle >>> crows = [0, 2, 3, 5] >>> cols = [1, 3, 2, 0, 1] >>> values = [1, 2, 3, 4, 5] >>> dense_shape = [3, 4] >>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape) >>> csr.crows() Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True, [0, 2, 3, 5]) )DOC"); // NOLINT static PyObject* tensor_method_get_non_zero_crows(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE(self->tensor.is_sparse_csr_tensor(), common::errors::Fatal( "this method is only effective for SparseCsrTensor")); auto sparse_csr_tensor = std::dynamic_pointer_cast(self->tensor.impl()); Tensor tensor( std::make_shared(sparse_csr_tensor->non_zero_crows())); return ToPyObject(tensor); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_cols__doc__, R"DOC(cols($self, /) -- Note: **This API is only available for SparseCsrTensor.** Returns the column index of non zero elements in input SparseCsrTensor. Returns: DenseTensor Examples: .. code-block:: pycon >>> import paddle >>> crows = [0, 2, 3, 5] >>> cols = [1, 3, 2, 0, 1] >>> values = [1, 2, 3, 4, 5] >>> dense_shape = [3, 4] >>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape) >>> csr.cols() Tensor(shape=[5], dtype=int64, place=Place(gpu:0), stop_gradient=True, [1, 3, 2, 0, 1]) )DOC"); // NOLINT static PyObject* tensor_method_get_non_zero_cols(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE(self->tensor.is_sparse_csr_tensor(), common::errors::Fatal( "this method is only effective for SparseCsrTensor")); auto sparse_csr_tensor = std::dynamic_pointer_cast(self->tensor.impl()); Tensor tensor( std::make_shared(sparse_csr_tensor->non_zero_cols())); return ToPyObject(tensor); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_is_dense__doc__, R"DOC(is_dense($self, /) -- Whether the Tensor is a Dense Tensor. Returns: Whether the Tensor is a Dense Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1.0], stop_gradient=False) >>> print(x.is_dense()) True )DOC"); // NOLINT static PyObject* tensor_method_is_dense(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { return ToPyObject(false); } return ToPyObject(self->tensor.is_dense_tensor()); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_is_dist__doc__, R"DOC(is_dist($self, /) -- Whether the Tensor is a Distributed Tensor. Returns: Whether the Tensor is a Distributed Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1.0], stop_gradient=False) >>> print(x.is_dist()) False )DOC"); // NOLINT static PyObject* tensor_method_is_dist(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { return ToPyObject(false); } return ToPyObject(self->tensor.is_dist_tensor()); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_is_sparse__doc__, // NOLINT R"DOC(is_sparse($self, /) -- Returns whether the input Tensor is SparseCooTensor or SparseCsrTensor. When input is SparseCooTensor/SparseCsrTensor, will return True. When input is DenseTensor, will return False. Returns: bool Examples: .. code-block:: pycon >>> import paddle >>> indices = [[0, 1, 2], [1, 2, 0]] >>> values = [1.0, 2.0, 3.0] >>> dense_shape = [3, 3] >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape) >>> coo.is_sparse() True )DOC"); // NOLINT static PyObject* tensor_method_is_sparse(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { return ToPyObject(false); } return ToPyObject(self->tensor.is_sparse_coo_tensor() || self->tensor.is_sparse_csr_tensor()); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_is_sparse_coo__doc__, // NOLINT R"DOC(is_sparse_coo($self, /) -- Returns whether the input Tensor is SparseCooTensor. When input is SparseCooTensor, will return True. When input is DenseTensor/SparseCsrTensor, will return False. Returns: bool Examples: .. code-block:: pycon >>> import paddle >>> indices = [[0, 1, 2], [1, 2, 0]] >>> values = [1.0, 2.0, 3.0] >>> dense_shape = [3, 3] >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape) >>> coo.is_sparse_coo() True )DOC"); // NOLINT static PyObject* tensor_method_is_sparse_coo(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { return ToPyObject(false); } return ToPyObject(self->tensor.is_sparse_coo_tensor()); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_is_sparse_csr__doc__, // NOLINT R"DOC(is_sparse_csr($self, /) -- Returns whether the input Tensor is SparseCsrTensor. When input is SparseCsrTensor, will return True. When input is DenseTensor/SparseCooTensor, will return False. Returns: bool Examples: .. code-block:: pycon >>> import paddle >>> crows = [0, 2, 3, 5] >>> cols = [1, 3, 2, 0, 1] >>> values = [1, 2, 3, 4, 5] >>> dense_shape = [3, 4] >>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape) >>> csr.is_sparse_csr() True )DOC"); // NOLINT static PyObject* tensor_method_is_sparse_csr(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { return ToPyObject(false); } return ToPyObject(self->tensor.is_sparse_csr_tensor()); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_to_sparse_csr__doc__, // NOLINT R"DOC(to_sparse_csr($self, /) -- Note: **This API is only available for DenseTensor or SparseCooTensor.** Convert input Tensor to SparseCsrTensor. When input is SparseCooTensor, will convert `COO` to `CSR` . When input is DenseTensor, will convert `Dense` to `CSR` . When input is SparseCsrTensor, the function will directly return the input itself without performing any conversion. Returns: SparseCsrTensor Examples: .. code-block:: pycon >>> import paddle >>> indices = [[0, 1, 2], [1, 2, 0]] >>> values = [1.0, 2.0, 3.0] >>> dense_shape = [3, 3] >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape) >>> coo.to_sparse_csr() Tensor(shape=[3, 3], dtype=paddle.float32, place=Place(gpu:0), stop_gradient=True, crows=[0, 1, 2, 3], cols=[1, 2, 0], values=[1., 2., 3.]) )DOC"); // NOLINT static PyObject* tensor_method_to_sparse_csr(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (self->tensor.is_sparse_csr_tensor()) { Py_INCREF(self); return reinterpret_cast(self); } auto csr_tensor = self->tensor.to_sparse_csr(); egr::EagerUtils::autograd_meta(&csr_tensor) ->SetStopGradient( egr::EagerUtils::autograd_meta(&self->tensor)->StopGradient()); egr::EagerUtils::autograd_meta(&csr_tensor) ->SetPersistable( egr::EagerUtils::autograd_meta(&(self->tensor))->Persistable()); return ToPyObject(csr_tensor); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_is_coalesced__doc__, // NOLINT R"DOC(is_coalesced($self, /) -- Check whether the Tensor is a coalesced SparseCooTensor. If not it will return False. Tensor types other than SparseCooTensor are not supported. Notes: It will return always False for a newly created SparseCooTensor. Args: x (Tensor): The input tensor. It can only be SparseCooTensor. Returns: bool: True if the Tensor is a coalesced SparseCooTensor, and False otherwise. Examples: .. code-block:: pycon >>> import paddle >>> indices = [[0, 0, 1], [1, 1, 2]] >>> values = [1.0, 2.0, 3.0] >>> x = paddle.sparse.sparse_coo_tensor(indices, values) >>> x.is_coalesced() False >>> x = x.coalesce() >>> x.is_coalesced() True >>> indices = [[0, 1, 1], [1, 0, 2]] >>> values = [1.0, 2.0, 3.0] >>> x = paddle.sparse.sparse_coo_tensor(indices, values) >>> x.is_coalesced() False )DOC"); // NOLINT static PyObject* tensor_method_is_coalesced(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (self->tensor.is_sparse_coo_tensor()) { auto sparse_coo_tensor = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(sparse_coo_tensor->coalesced()); } else { PADDLE_THROW(common::errors::InvalidType( "Method is_coalesced only support sparse coo tensor.")); } EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_is_same_shape__doc__, // NOLINT R"DOC(is_same_shape($self, y, /) -- Return the results of shape comparison between two Tensors, check whether x.shape equal to y.shape. Any two type Tensor among DenseTensor/SparseCooTensor/SparseCsrTensor are supported. Args: x (Tensor): The input tensor. It can be DenseTensor/SparseCooTensor/SparseCsrTensor. y (Tensor): The input tensor. It can be DenseTensor/SparseCooTensor/SparseCsrTensor. Returns: bool: True for same shape and False for different shape. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.rand([2, 3, 8]) >>> y = paddle.rand([2, 3, 8]) >>> y = y.to_sparse_csr() >>> z = paddle.rand([2, 5]) >>> x.is_same_shape(y) True >>> x.is_same_shape(z) False )DOC"); // NOLINT static PyObject* tensor_method_is_same_shape(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto other = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0); return ToPyObject(self->tensor.shape() == other.shape()); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__inplace_version(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY uint32_t inplace_version = self->tensor.current_inplace_version(); return ToPyObject(inplace_version); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_element_size__doc__, // NOLINT R"DOC(element_size($self, /) -- Returns the size in bytes of an element in the Tensor. Returns: int, The size in bytes of an element in the Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor(1, dtype='bool') >>> x.element_size() 1 >>> x = paddle.to_tensor(1, dtype='float16') >>> x.element_size() 2 >>> x = paddle.to_tensor(1, dtype='float32') >>> x.element_size() 4 >>> x = paddle.to_tensor(1, dtype='float64') >>> x.element_size() 8 >>> x = paddle.to_tensor(1, dtype='complex128') >>> x.element_size() 16 )DOC"); static PyObject* tensor_method_element_size(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY uint32_t element_size = phi::SizeOf(self->tensor.dtype()); return ToPyObject(element_size); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method__bump_inplace_version__doc__, // NOLINT R"DOC(_bump_inplace_version($self, /) -- Note: **This API is ONLY available in Dygraph mode.** **This is a very low level API. Users should not use it directly. ** Bump the version whenever the Tensor is modified through an inplace operation. )DOC"); static PyObject* tensor__bump_inplace_version(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY self->tensor.bump_inplace_version(); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_is_selected_rows(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (!self->tensor.defined()) { return ToPyObject(false); } return ToPyObject(self->tensor.is_selected_rows()); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method_get_rows(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY PADDLE_ENFORCE( self->tensor.is_selected_rows(), common::errors::Fatal("this method is only effective for SelectedRows")); auto selected_rows = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(selected_rows->rows()); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__reset_grad_inplace_version(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Py_ssize_t args_num = PyTuple_Size(args); bool set_to_zero = true; if (args_num == (Py_ssize_t)1) { set_to_zero = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 0), 0); } Tensor* grad = egr::EagerUtils::mutable_grad(self->tensor); if (grad && grad->defined() && grad->initialized() && (grad->is_dense_tensor() || grad->is_dist_tensor())) { grad->reset_inplace_version(set_to_zero); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method__share_memory(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY #ifndef _WIN32 PADDLE_ENFORCE_EQ(phi::is_cpu_place(self->tensor.place()), true, common::errors::InvalidArgument( "Sharing memory only support CPU Tensor currently")); // 1. get DenseTensor auto* t = std::dynamic_pointer_cast(self->tensor.impl()).get(); // 2. allocate shared memory void* data_ptr = t->data(); size_t data_size = t->numel() * framework::SizeOfType(framework::TransToProtoVarType(t->dtype())); auto shared_writer_holder = memory::allocation::AllocateMemoryMapWriterAllocation(data_size); // 3. maintain mmap fd set & backup ipc_name const std::string& ipc_name = shared_writer_holder->ipc_name(); memory::allocation::MemoryMapFdSet::Instance().Insert(ipc_name); // 4. copy data & reset holder memory::Copy( CPUPlace(), shared_writer_holder->ptr(), CPUPlace(), data_ptr, data_size); t->ResetHolder(shared_writer_holder); return ToPyObject(t); #else PADDLE_THROW(common::errors::PermissionDenied( "Sharing memory in Windows OS is not supported currently")); RETURN_PY_NONE #endif EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__offset(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY phi::DenseTensor* dense_tensor = nullptr; if (self->tensor.is_dist_tensor()) { dense_tensor = static_cast(self->tensor.impl().get()) ->unsafe_mutable_value(); } else { dense_tensor = static_cast(self->tensor.impl().get()); } PADDLE_ENFORCE_EQ( dense_tensor->IsInitialized(), true, common::errors::InvalidArgument("Tensor %s has not been initialized!", self->tensor.name())); return ToPyObject(dense_tensor->offset()); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__grad_name(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor* grad = egr::EagerUtils::mutable_grad(self->tensor); PADDLE_ENFORCE_EQ( grad != nullptr, true, common::errors::InvalidArgument( "Detected nullptr grad. Please check if you have manually " "cleared the grad inside autograd_meta")); return ToPyObject(grad->name()); EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__grad_value(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor* grad = egr::EagerUtils::mutable_grad(self->tensor); PADDLE_ENFORCE_EQ( grad != nullptr, true, common::errors::InvalidArgument( "Detected nullptr grad. Please check if you have manually " "cleared the grad inside autograd_meta")); if (!grad->defined()) { RETURN_PY_NONE } if (grad->is_dense_tensor()) { auto* grad_tensor = static_cast(grad->impl().get()); return ToPyObject(grad_tensor); } else if (grad->is_dist_tensor()) { auto* grad_tensor = static_cast(self->tensor.impl().get()) ->unsafe_mutable_value(); return ToPyObject(grad_tensor); } else { PADDLE_THROW(common::errors::Fatal( "This method is only supported for DenseTensor and DistTensor.")); RETURN_PY_NONE } EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__local_value(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (self->tensor.is_dist_tensor()) { #ifdef PADDLE_WITH_DISTRIBUTE phi::distributed::DistTensor* dist_tensor = static_cast(self->tensor.impl().get()); Tensor result(std::make_shared(dist_tensor->value())); return ToPyObject(result); #else PADDLE_THROW(common::errors::Unavailable( "The `_local_value` method of (Dist)Tensor is not supported " "in the current PaddlePaddle, please recompile and install " "PaddlePaddle " "with the option of `WITH_DISTRIBUTE=ON`.")); #endif } else { RETURN_PY_NONE } EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__unset_fake_empty(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY Tensor* grad = egr::EagerUtils::mutable_grad(self->tensor); PADDLE_ENFORCE_EQ( grad != nullptr, true, common::errors::InvalidArgument( "Detected nullptr grad. Please check if you have manually " "cleared the grad inside autograd_meta")); bool is_leaf = egr::EagerUtils::IsLeafTensor(self->tensor); if (is_leaf) { std::static_pointer_cast( egr::EagerUtils::grad_node(self->tensor)) ->SetFakeEmpty(false); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_data_ptr__doc__, // NOLINT R"DOC(data_ptr($self, /) -- Returns the address of the first element of current Tensor. Returns: int, The address of the first element of current Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1, 2, 3]) >>> print(x.data_ptr()) >>> # doctest: +SKIP('return the address') 93220864 >>> # doctest: -SKIP )DOC"); // NOLINT static PyObject* tensor_data_ptr(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (self->tensor.defined() && self->tensor.has_allocation() && self->tensor.is_dense_tensor()) { return ToPyObject( (int64_t)std::dynamic_pointer_cast( // NOLINT self->tensor.impl()) ->data()); } else if (self->tensor.defined() && self->tensor.has_allocation() && self->tensor.is_dist_tensor()) { return ToPyObject( (int64_t) std::dynamic_pointer_cast( // NOLINT self->tensor.impl()) ->unsafe_mutable_value() ->data()); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor__grad_ivar(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(6) << "Get grad for tensor: " << self->tensor.name(); auto meta = egr::EagerUtils::nullable_autograd_meta(self->tensor); VLOG(6) << meta << " has_allocation: " << meta->Grad().has_allocation(); if (meta && meta->Grad().has_allocation()) { return ToPyObject(meta->Grad()); } else { if (meta && !meta->Grad().has_allocation() && meta->Grad().impl() && meta->Grad().is_dist_tensor()) { return ToPyObject(meta->Grad(), false); } RETURN_PY_NONE } EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_get_strides__doc__, // NOLINT R"DOC(get_strides($self, /) -- Returns the strides of current Tensor. Returns: List, the strides of current Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1, 2, 3]) >>> y = x[1] >>> print(y.get_strides()) [] )DOC"); // NOLINT static PyObject* tensor_method_get_strides(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY std::vector value; if (!self->tensor.defined() || (!self->tensor.is_dense_tensor() && !self->tensor.is_dist_tensor())) { return ToPyObject(value); } auto stride = self->tensor.strides(); int rank = static_cast(stride.size()); value.resize(rank); for (int i = 0; i < rank; i++) { value[i] = stride[i]; } return ToPyObject(value); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_stride__doc__, // NOLINT R"DOC(stride($self, dim=None, /) -- Returns the stride of self tensor. Stride is the jump necessary to go from one element to the next one in the specified dimension dim. A tuple of all strides is returned when no argument is passed in. Otherwise, an integer value is returned as the stride in the particular dimension dim. Args: dim (int, optional): If specified, return the stride in the particular dimension dim. If None, return the strides of all dimensions. Default: None. Returns: int or tuple: The stride of the tensor. If dim is None, returns a tuple of all strides. If dim is specified, returns the stride in that dimension. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([[1, 2, 3], [4, 5, 6]]) >>> x.stride() [3, 1] >>> x.stride(0) 3 >>> x.stride(1) 1 >>> x.stride(-1) 1 )DOC"); // NOLINT static PyObject* tensor_method_stride(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY static char* kwlist[] = {const_cast("dim"), nullptr}; PyObject* dim_obj = nullptr; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &dim_obj)) { RETURN_PY_NONE } std::vector value; if (!self->tensor.defined() || (!self->tensor.is_dense_tensor() && !self->tensor.is_dist_tensor())) { return ToPyObject(value); } auto stride = self->tensor.strides(); int rank = static_cast(stride.size()); value.resize(rank); for (int i = 0; i < rank; i++) { value[i] = stride[i]; } if (dim_obj == nullptr || dim_obj == Py_None) { return ToPyObject(value); } if (!PyLong_Check(dim_obj)) { PADDLE_THROW(common::errors::InvalidArgument("dim must be an integer")); } int dim = static_cast(PyLong_AsLong(dim_obj)); dim = dim < 0 ? dim + rank : dim; PADDLE_ENFORCE_EQ( dim >= 0 && dim < rank, true, common::errors::InvalidArgument( "Dimension out of range (expected to be in range of [%d, %d], " "but got %d)", -rank, rank - 1, static_cast(PyLong_AsLong(dim_obj)))); return ToPyObject(value[dim]); EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_contiguous__doc__, // NOLINT R"DOC(contiguous($self, /) -- Returns a contiguous in memory tensor containing the same data as current Tensor. If self tensor is already contiguous, this function returns the current Tensor. Returns: Tensor, The contiguous Tensor. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1, 2, 3]) >>> y = x[1] >>> y = y.contiguous() >>> print(y) Tensor(shape=[], dtype=int64, place=Place(cpu), stop_gradient=True, 2) )DOC"); // NOLINT static PyObject* tensor_contiguous(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (self->tensor.is_dense_tensor() || self->tensor.is_dist_tensor()) { phi::DenseTensor* dense_tensor = nullptr; if (self->tensor.is_dist_tensor()) { dense_tensor = static_cast(self->tensor.impl().get()) ->unsafe_mutable_value(); } else { dense_tensor = static_cast(self->tensor.impl().get()); } if (dense_tensor->meta().is_contiguous()) { Py_INCREF(self); return reinterpret_cast(self); } else { eager_gil_scoped_release guard; EagerSetDeviceId(); *dense_tensor = paddle::experimental::Trans2Contiguous(*dense_tensor); Py_INCREF(self); return reinterpret_cast(self); } } else { Py_INCREF(self); return reinterpret_cast(self); } EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_is_contiguous__doc__, // NOLINT R"DOC(is_contiguous($self, /) -- Whether the Tensor is contiguous. Returns: Bool, Whether the Tensor is contiguous. Examples: .. code-block:: pycon >>> import paddle >>> x = paddle.to_tensor([1, 2, 3]) >>> y = x[1] >>> print(y.is_contiguous()) )DOC"); // NOLINT static PyObject* tensor_is_contiguous(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (self->tensor.is_dense_tensor()) { auto dense_tensor = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(dense_tensor->meta().is_contiguous()); } else if (self->tensor.is_dist_tensor()) { auto dense_tensor = std::dynamic_pointer_cast( self->tensor.impl()) ->unsafe_mutable_value(); return ToPyObject(dense_tensor->meta().is_contiguous()); } else { return ToPyObject(true); } EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_sparse_dim__doc__, R"DOC(sparse_dim($self, /) -- Returns the number of sparse dimensions of sparse Tensor. Note: **If self is not sparse Tensor, return 0.** Returns: int, sparse dim of self Tensor Examples: .. code-block:: pycon >>> import paddle >>> indices = [[0, 1, 2], [1, 2, 0]] >>> values = [1.0, 2.0, 3.0] >>> dense_shape = [3, 3] >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape) >>> coo.sparse_dim() 2 >>> crows = [0, 2, 3, 5] >>> cols = [1, 3, 2, 0, 1] >>> values = [1, 2, 3, 4, 5] >>> dense_shape = [3, 4] >>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape) >>> csr.sparse_dim() 2 >>> dense = paddle.to_tensor([1, 2, 3]) >>> dense.sparse_dim() 0 )DOC"); // NOLINT static PyObject* tensor_method_sparse_dim(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (self->tensor.is_sparse_coo_tensor()) { auto sparse_coo_tensor = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(sparse_coo_tensor->sparse_dim()); } else if (self->tensor.is_sparse_csr_tensor()) { auto sparse_csr_tensor = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(sparse_csr_tensor->sparse_dim()); } else { return ToPyObject(0); } EAGER_CATCH_AND_THROW_RETURN_NULL } PyDoc_STRVAR(tensor_method_dense_dim__doc__, R"DOC(dense_dim($self, /) -- Returns the number of dense dimensions of sparse Tensor. Note: **If self is not sparse Tensor, return len(self.shape).** Returns: int, dense dim of self Tensor Examples: .. code-block:: pycon >>> import paddle >>> import numpy as np >>> indices = [[0, 1, 1], [2, 0, 2]] >>> values = np.array([[3, 4], [5, 6], [7, 8]]) >>> dense_shape = [2, 3, 2] >>> coo = paddle.sparse.sparse_coo_tensor(indices, values, dense_shape) >>> coo.dense_dim() 1 >>> crows = [0, 2, 3, 5] >>> cols = [1, 3, 2, 0, 1] >>> values = [1, 2, 3, 4, 5] >>> dense_shape = [3, 4] >>> csr = paddle.sparse.sparse_csr_tensor(crows, cols, values, dense_shape) >>> csr.dense_dim() 0 >>> dense = paddle.to_tensor([[1, 2, 3]]) >>> dense.dense_dim() >>> 2 )DOC"); // NOLINT static PyObject* tensor_method_dense_dim(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY if (self->tensor.is_sparse_coo_tensor()) { auto sparse_coo_tensor = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(sparse_coo_tensor->dense_dim()); } else if (self->tensor.is_sparse_csr_tensor()) { auto sparse_csr_tensor = std::dynamic_pointer_cast(self->tensor.impl()); return ToPyObject(sparse_csr_tensor->dense_dim()); } else { return ToPyObject(self->tensor.shape().size()); } EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method__set_impl(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(4) << "Running in tensor_method__set_impl: set Tensor impl form the " "other Tensor."; auto tensor = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0); self->tensor.set_impl(tensor.impl()); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } #if defined(PADDLE_WITH_CUDA) static PyObject* tensor_method__record_stream(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(4) << "Running in tensor_method__record_stream: record stream for Tensor."; auto* tensor = static_cast(self->tensor.impl().get()); if (tensor) { const auto& device_id = paddle::platform::GetCurrentDeviceId(); auto stream = paddle::platform::get_current_stream(device_id)->raw_stream(); memory::RecordStream(tensor->Holder(), stream); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } static PyObject* tensor_method__uva(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(4) << "Running in tensor_method__uva."; PADDLE_ENFORCE_EQ( self->tensor.is_dense_tensor() || self->tensor.is_dist_tensor(), true, common::errors::InvalidArgument("Unified virtual addressing only support " "DenseTensor and DistTensor currently.")); PADDLE_ENFORCE_EQ( phi::is_cpu_place(self->tensor.place()), true, common::errors::InvalidArgument("Unified virtual addressing only support " "CPU Tensor currently.")); int device_id = pybind::CastPyArg2AttrLong(PyTuple_GET_ITEM(args, 0), 0); // NOLINT phi::DenseTensor* dense_tensor = nullptr; if (self->tensor.is_dist_tensor()) { dense_tensor = static_cast(self->tensor.impl().get()) ->unsafe_mutable_value(); } else { dense_tensor = static_cast(self->tensor.impl().get()); } tensor_uva(dense_tensor, device_id); RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } #endif #if defined(PADDLE_WITH_XPU) static PyObject* tensor_method__record_stream(TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY VLOG(4) << "Running in tensor_method__record_stream: record stream for Tensor."; auto* tensor = static_cast(self->tensor.impl().get()); if (tensor) { const auto& device_id = paddle::platform::GetXPUCurrentDeviceId(); auto place = phi::XPUPlace(device_id); auto* dev_ctx = static_cast( phi::DeviceContextPool::Instance().Get(place)); auto stream = dev_ctx->get_current_stream_handle()->raw_stream(); memory::RecordStream(tensor->Holder(), stream); } RETURN_PY_NONE EAGER_CATCH_AND_THROW_RETURN_NULL } #endif static PyObject* tensor_method__is_string_tensor_hold_allocation( TensorObject* self, PyObject* args, PyObject* kwargs) { EAGER_TRY auto string_tensor = std::dynamic_pointer_cast(self->tensor.impl()); if (string_tensor) { return ToPyObject(string_tensor->initialized()); } else { return ToPyObject(false); } EAGER_CATCH_AND_THROW_RETURN_NULL } PyMethodDef variable_methods[] = { // NOLINT {"numpy", (PyCFunction)(void (*)())tensor_method_numpy, METH_VARARGS | METH_KEYWORDS, tensor_method_numpy__doc__}, {"_is_initialized", (PyCFunction)(void (*)())tensor_method__is_initialized, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_is_dense_tensor_hold_allocation", (PyCFunction)(void (*)())tensor_method__is_dense_tensor_hold_allocation, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_copy_to", (PyCFunction)(void (*)())tensor_method__copy_to, METH_VARARGS | METH_KEYWORDS, nullptr}, {"copy_", (PyCFunction)(void (*)())tensor_method_copy_, METH_VARARGS | METH_KEYWORDS, nullptr}, {"clone", (PyCFunction)(void (*)())tensor_method_clone, METH_VARARGS | METH_KEYWORDS, tensor_method_clone__doc__}, {"_new_shared_tensor", (PyCFunction)(void (*)())tensor_method__new_shared_tensor, METH_VARARGS | METH_KEYWORDS, tensor_method__new_shared_tensor__doc__}, {"reconstruct_from_", (PyCFunction)(void (*)())tensor_method_reconstruct_from_, METH_VARARGS | METH_KEYWORDS, tensor_reconstruct_from___doc__}, {"retain_grads", (PyCFunction)(void (*)())tensor_retain_grads, METH_VARARGS | METH_KEYWORDS, tensor_method_retain_grads__doc__}, {"retain_grad", (PyCFunction)(void (*)())tensor_retain_grads, METH_VARARGS | METH_KEYWORDS, tensor_method_retain_grad__doc__}, {"clear_gradient", (PyCFunction)(void (*)())tensor_clear_gradient, METH_VARARGS | METH_KEYWORDS, tensor_clear_gradient__doc__}, {"is_dense", (PyCFunction)(void (*)())tensor_method_is_dense, METH_VARARGS | METH_KEYWORDS, tensor_method_is_dense__doc__}, {"is_dist", (PyCFunction)(void (*)())tensor_method_is_dist, METH_VARARGS | METH_KEYWORDS, tensor_method_is_dist__doc__}, {"_zero_grads", (PyCFunction)(void (*)())tensor__zero_grads, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_to_dist_", (PyCFunction)(void (*)())tensor__to_dist, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_share_buffer_to", (PyCFunction)(void (*)())tensor__share_buffer_to, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_unsafe_share_buffer_to", (PyCFunction)(void (*)())tensor__unsafe_share_buffer_to, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_is_shared_buffer_with", (PyCFunction)(void (*)())tensor__is_shared_buffer_with, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_share_underline_tensor_to", (PyCFunction)(void (*)())tensor__share_underline_tensor_to, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_is_shared_underline_tensor_with", (PyCFunction)(void (*)())tensor__is_shared_underline_tensor_with, METH_VARARGS | METH_KEYWORDS, nullptr}, {"detach", (PyCFunction)(void (*)())tensor_method_detach, METH_VARARGS | METH_KEYWORDS, tensor_method_detach__doc__}, {"detach_", (PyCFunction)(void (*)())tensor_method_detach_, METH_VARARGS | METH_KEYWORDS, tensor_method_detach___doc__}, {"get_tensor", (PyCFunction)(void (*)())tensor_method_get_underline_tensor, METH_VARARGS | METH_KEYWORDS, tensor_method_get_tensor__doc__}, {"get_selected_rows", (PyCFunction)(void (*)())tensor_method_get_underline_selected_rows, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_get_tensor_from_selected_rows", (PyCFunction)(void (*)())tensor_method__get_tensor_from_selected_rows, METH_VARARGS | METH_KEYWORDS, nullptr}, {"set_tensor", (PyCFunction)(void (*)())tensor_method_set_underline_tensor, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_getitem_dygraph", (PyCFunction)(void (*)())tensor__getitem_dygraph, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_getitem_from_offset", (PyCFunction)(void (*)())tensor__getitem_from_offset, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_setitem_dygraph", (PyCFunction)(void (*)())tensor__setitem_dygraph, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_apply", (PyCFunction)(void (*)())tensor_apply, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_apply_", (PyCFunction)(void (*)())tensor_apply_, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_register_grad_hook", (PyCFunction)(void (*)())tensor_register_grad_hook, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_inplace_assign", // NOTE(xiongkun03): only used in sot. (PyCFunction)(void (*)())tensor_inplace_assign, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_remove_grad_hook", (PyCFunction)(void (*)())tensor_remove_grad_hook, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_apply_backward_hook", (PyCFunction)(void (*)())apply_backward_hook, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_register_backward_hook", (PyCFunction)(void (*)())tensor_register_reduce_hook, METH_VARARGS | METH_KEYWORDS, tensor_method__register_reduce_hook__doc__}, {"_set_grad_type", (PyCFunction)(void (*)())tensor__set_grad_type, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_clear", (PyCFunction)(void (*)())tensor__clear, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_clear_dataptr", (PyCFunction)(void (*)())tensor__clear_dataptr, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_clear_to_zero_allocation", (PyCFunction)(void (*)())tensor__clear_to_zero_allocation, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_holder_size", (PyCFunction)(void (*)())tensor__holder_size, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_copy_gradient_from", (PyCFunction)(void (*)())tensor__copy_gradient_from, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_tensor_use_gpudnn", (PyCFunction)(void (*)())tensor__use_gpudnn, METH_VARARGS | METH_KEYWORDS, nullptr}, /** the methods to adapt old dygraph, will be removed in the future **/ {"set_string_list", (PyCFunction)(void (*)())tensor_method_set_string_list, METH_VARARGS | METH_KEYWORDS, nullptr}, {"set_vocab", (PyCFunction)(void (*)())tensor_method_set_vocab, METH_VARARGS | METH_KEYWORDS, nullptr}, {"get_map_tensor", (PyCFunction)(void (*)())tensor_method_get_map_tensor, METH_VARARGS | METH_KEYWORDS, nullptr}, /***the method of sparse tensor****/ {"nnz", (PyCFunction)(void (*)())tensor_method_get_non_zero_nums, METH_VARARGS | METH_KEYWORDS, tensor_method_nnz__doc__}, {"indices", (PyCFunction)(void (*)())tensor_method_get_non_zero_indices, METH_VARARGS | METH_KEYWORDS, tensor_method_indices__doc__}, {"values", (PyCFunction)(void (*)())tensor_method_get_non_zero_elements, METH_VARARGS | METH_KEYWORDS, tensor_method_values__doc__}, {"crows", (PyCFunction)(void (*)())tensor_method_get_non_zero_crows, METH_VARARGS | METH_KEYWORDS, tensor_method_crows__doc__}, {"cols", (PyCFunction)(void (*)())tensor_method_get_non_zero_cols, METH_VARARGS | METH_KEYWORDS, tensor_method_cols__doc__}, {"is_sparse", (PyCFunction)(void (*)())tensor_method_is_sparse, METH_VARARGS | METH_KEYWORDS, tensor_is_sparse__doc__}, {"is_sparse_coo", (PyCFunction)(void (*)())tensor_method_is_sparse_coo, METH_VARARGS | METH_KEYWORDS, tensor_is_sparse_coo__doc__}, {"is_sparse_csr", (PyCFunction)(void (*)())tensor_method_is_sparse_csr, METH_VARARGS | METH_KEYWORDS, tensor_is_sparse_csr__doc__}, {"is_same_shape", (PyCFunction)(void (*)())tensor_method_is_same_shape, METH_VARARGS | METH_KEYWORDS, tensor_is_same_shape__doc__}, {"to_sparse_csr", (PyCFunction)(void (*)())tensor_method_to_sparse_csr, METH_VARARGS | METH_KEYWORDS, tensor_to_sparse_csr__doc__}, {"is_coalesced", (PyCFunction)(void (*)())tensor_method_is_coalesced, METH_VARARGS | METH_KEYWORDS, tensor_is_coalesced__doc__}, {"sparse_dim", (PyCFunction)(void (*)())tensor_method_sparse_dim, METH_VARARGS | METH_KEYWORDS, tensor_method_sparse_dim__doc__}, {"dense_dim", (PyCFunction)(void (*)())tensor_method_dense_dim, METH_VARARGS | METH_KEYWORDS, tensor_method_dense_dim__doc__}, /***the method of sparse tensor****/ {"element_size", (PyCFunction)(void (*)())tensor_method_element_size, METH_VARARGS | METH_KEYWORDS, tensor_method_element_size__doc__}, {"_inplace_version", (PyCFunction)(void (*)())tensor__inplace_version, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_bump_inplace_version", (PyCFunction)(void (*)())tensor__bump_inplace_version, METH_VARARGS | METH_KEYWORDS, tensor_method__bump_inplace_version__doc__}, {"is_selected_rows", (PyCFunction)(void (*)())tensor_method_is_selected_rows, METH_VARARGS | METH_KEYWORDS, nullptr}, {"rows", (PyCFunction)(void (*)())tensor_method_get_rows, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_reset_grad_inplace_version", (PyCFunction)(void (*)())tensor__reset_grad_inplace_version, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_share_memory", (PyCFunction)(void (*)())tensor_method__share_memory, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_offset", (PyCFunction)(void (*)())tensor__offset, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_grad_name", (PyCFunction)(void (*)())tensor__grad_name, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_grad_value", (PyCFunction)(void (*)())tensor__grad_value, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_local_value", (PyCFunction)(void (*)())tensor__local_value, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_unset_fake_empty", (PyCFunction)(void (*)())tensor__unset_fake_empty, METH_VARARGS | METH_KEYWORDS, nullptr}, {"data_ptr", (PyCFunction)(void (*)())tensor_data_ptr, METH_VARARGS | METH_KEYWORDS, tensor_data_ptr__doc__}, {"_grad_ivar", (PyCFunction)(void (*)())tensor__grad_ivar, METH_VARARGS | METH_KEYWORDS, nullptr}, {"contiguous", (PyCFunction)(void (*)())tensor_contiguous, METH_VARARGS | METH_KEYWORDS, tensor_contiguous__doc__}, {"is_contiguous", (PyCFunction)(void (*)())tensor_is_contiguous, METH_VARARGS | METH_KEYWORDS, tensor_is_contiguous__doc__}, {"get_strides", (PyCFunction)(void (*)())tensor_method_get_strides, METH_VARARGS | METH_KEYWORDS, tensor_get_strides__doc__}, {"stride", (PyCFunction)(void (*)())tensor_method_stride, METH_VARARGS | METH_KEYWORDS, tensor_stride__doc__}, {"_set_impl", (PyCFunction)(void (*)())tensor_method__set_impl, METH_VARARGS | METH_KEYWORDS, nullptr}, #if defined(PADDLE_WITH_CUDA) {"_record_stream", (PyCFunction)(void (*)())tensor_method__record_stream, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_tensor_uva", (PyCFunction)(void (*)())tensor_method__uva, METH_VARARGS | METH_KEYWORDS, nullptr}, #endif #if defined(PADDLE_WITH_XPU) {"_record_stream", (PyCFunction)(void (*)())tensor_method__record_stream, METH_VARARGS | METH_KEYWORDS, nullptr}, #endif {nullptr, nullptr, 0, nullptr}}; // variable_methods for core.eager.StringTensor PyMethodDef string_tensor_variable_methods[] = { // NOLINT {"numpy", (PyCFunction)(void (*)())tensor_method_numpy_for_string_tensor, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_is_initialized", (PyCFunction)(void (*)())tensor_method__is_initialized, METH_VARARGS | METH_KEYWORDS, nullptr}, {"_is_string_tensor_hold_allocation", (PyCFunction)(void (*)())tensor_method__is_string_tensor_hold_allocation, METH_VARARGS | METH_KEYWORDS, nullptr}, // TODO(zhoushunjie): Need to add _copy_to, copy_ for StringTensor. {nullptr, nullptr, 0, nullptr}}; } // namespace paddle::pybind