4261 lines
150 KiB
C++
4261 lines
150 KiB
C++
/* 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 <BaseTsd.h>
|
|
typedef SSIZE_T ssize_t;
|
|
#endif
|
|
|
|
#include <Python.h>
|
|
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#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<phi::DenseTensor*>(
|
|
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<int64_t> 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<int>(tensor_dims.size()) - 1; i >= 0; --i) {
|
|
py_dims[i] = static_cast<Py_intptr_t>(tensor_dims[i]);
|
|
py_strides[i] = static_cast<Py_intptr_t>(sizeof_dtype * tensor_stride[i]);
|
|
numel *= py_dims[i];
|
|
}
|
|
} else {
|
|
for (int i = static_cast<int>(tensor_dims.size()) - 1; i >= 0; --i) {
|
|
py_dims[i] = static_cast<Py_intptr_t>(tensor_dims[i]);
|
|
py_strides[i] = static_cast<Py_intptr_t>(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<int>(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<phi::SelectedRows*>(self->tensor.impl().get());
|
|
auto* dense_tensor =
|
|
static_cast<phi::DenseTensor*>(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<phi::Allocation>(
|
|
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<phi::distributed::DistTensor*>(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<phi::Allocation>(
|
|
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<DenseTensor>(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<phi::Allocation>(
|
|
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<phi::SelectedRows*>(self->tensor.impl().get());
|
|
auto* dense_tensor =
|
|
static_cast<phi::DenseTensor*>(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<phi::Allocation>(
|
|
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<phi::distributed::DistTensor*>(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<phi::Allocation>(
|
|
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<DenseTensor>(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<phi::Allocation>(
|
|
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<phi::SelectedRows*>(self->tensor.impl().get());
|
|
auto* dense_tensor =
|
|
static_cast<phi::DenseTensor*>(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<phi::Allocation>(
|
|
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<phi::distributed::DistTensor*>(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<phi::Allocation>(
|
|
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<DenseTensor>(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<phi::Allocation>(
|
|
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<phi::SelectedRows*>(self->tensor.impl().get());
|
|
auto* dense_tensor =
|
|
static_cast<phi::DenseTensor*>(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<phi::Allocation>(
|
|
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<DenseTensor>(self->tensor.impl());
|
|
// TODO(qili93): temporary for ascend npu performance to be removed along
|
|
// with npu_identity op
|
|
Tensor temp_tensor(std::make_shared<DenseTensor>());
|
|
if (dense_tensor->storage_properties_initialized()) {
|
|
temp_tensor = npu_identity_ad_func(self->tensor, -1);
|
|
dense_tensor =
|
|
std::dynamic_pointer_cast<DenseTensor>(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<phi::Allocation>(
|
|
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<DenseTensor>(std::move(cpu_tensor))));
|
|
uintptr_t ptr = reinterpret_cast<uintptr_t>(array_buffer) + array_offset;
|
|
PyObject* array = api.PyArray_NewFromDescr_(
|
|
api.PyArray_Type_,
|
|
api.PyArray_DescrFromType_(numpy_dtype),
|
|
static_cast<int>(py_rank),
|
|
py_dims,
|
|
py_strides,
|
|
reinterpret_cast<void*>(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<phi::StringTensor>(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<uint32_t[]>(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<DenseTensor>(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<phi::distributed::DistTensor*>(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<char*>("other"),
|
|
const_cast<char*>("blocking"),
|
|
const_cast<char*>("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<int>(PyTuple_Size(args)) : 0;
|
|
int remaining_kwargs = kwargs ? static_cast<int>(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<TensorObject*>(obj);
|
|
new (&(v->tensor)) Tensor();
|
|
if (retain_holder) {
|
|
v->tensor.set_impl(self->tensor.impl());
|
|
} else {
|
|
auto* dense_tensor =
|
|
dynamic_cast<phi::DenseTensor*>(self->tensor.impl().get());
|
|
if (dense_tensor != nullptr && dense_tensor->Holder() != nullptr) {
|
|
auto tmp = std::make_shared<DenseTensor>(
|
|
std::make_shared<phi::Allocation>(
|
|
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<egr::GradNodeAccumulation>(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<phi::SelectedRows>(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<phi::DenseTensor*>(grad->impl().get());
|
|
} else {
|
|
grad_t =
|
|
static_cast<phi::distributed::DistTensor*>(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::GradNodeAccumulation>(
|
|
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<phi::DenseTensor*>(grad->impl().get());
|
|
} else {
|
|
t = static_cast<phi::distributed::DistTensor*>(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<phi::DenseTensor*>(meta->MutableGrad()->impl().get());
|
|
} else {
|
|
t = static_cast<phi::distributed::DistTensor*>(
|
|
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<DenseTensor>(self->tensor.impl());
|
|
auto dist_tensor_ptr = std::make_shared<phi::distributed::DistTensor>(
|
|
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<TensorObject*>(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<phi::distributed::DistTensor*>(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<phi::distributed::DistTensor>());
|
|
}
|
|
auto dst_tensor =
|
|
static_cast<phi::distributed::DistTensor*>(dst_ptr->impl().get())
|
|
->unsafe_mutable_value();
|
|
dst_tensor->ShareBufferWith(*src_tensor);
|
|
dst_tensor->ShareDataTypeWith(*src_tensor);
|
|
} else {
|
|
auto* src_tensor =
|
|
static_cast<phi::DenseTensor*>(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<DenseTensor>());
|
|
}
|
|
auto dst_tensor = static_cast<phi::DenseTensor*>(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<TensorObject*>(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<phi::distributed::DistTensor*>(self->tensor.impl().get())
|
|
->unsafe_mutable_value();
|
|
if (!dst_ptr->defined()) {
|
|
dst_ptr->set_impl(std::make_shared<phi::distributed::DistTensor>());
|
|
}
|
|
auto dst_tensor =
|
|
static_cast<phi::distributed::DistTensor*>(dst_ptr->impl().get())
|
|
->unsafe_mutable_value();
|
|
dst_tensor->ShareBufferWith(*src_tensor);
|
|
dst_tensor->ShareDataTypeWith(*src_tensor);
|
|
} else {
|
|
auto* src_tensor =
|
|
static_cast<phi::DenseTensor*>(self->tensor.impl().get());
|
|
if (!dst_ptr->defined()) {
|
|
dst_ptr->set_impl(std::make_shared<DenseTensor>());
|
|
}
|
|
auto dst_tensor = static_cast<phi::DenseTensor*>(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<TensorObject*>(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<phi::distributed::DistTensor*>(self->tensor.impl().get())
|
|
->unsafe_mutable_value();
|
|
auto dst_tensor =
|
|
static_cast<phi::distributed::DistTensor*>(dst_ptr->impl().get())
|
|
->unsafe_mutable_value();
|
|
res = dst_tensor->IsSharedBufferWith(*self_ptr);
|
|
return ToPyObject(res);
|
|
} else {
|
|
auto* self_ptr = static_cast<phi::DenseTensor*>(self->tensor.impl().get());
|
|
auto dst_tensor = static_cast<phi::DenseTensor*>(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<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor);
|
|
if (!self->tensor.has_allocation()) {
|
|
PADDLE_ENFORCE(
|
|
self->tensor.is_dist_tensor() &&
|
|
!phi::distributed::IsCurRankInMesh(
|
|
static_cast<phi::distributed::DistTensor*>(
|
|
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<TensorObject*>(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<egr::AutogradMeta>();
|
|
autograd_meta->SetPersistable(
|
|
egr::EagerUtils::autograd_meta(&(self->tensor))->Persistable());
|
|
self->tensor.set_autograd_meta(autograd_meta);
|
|
Py_INCREF(reinterpret_cast<PyObject*>(self));
|
|
return reinterpret_cast<PyObject*>(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<phi::DenseTensor*>(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<phi::distributed::DistTensor*>(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<phi::DenseTensor*>(value.impl().get());
|
|
if (self->tensor.is_dense_tensor()) {
|
|
auto* dst_tensor =
|
|
static_cast<phi::DenseTensor*>(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<data_t>(
|
|
*src_tensor,
|
|
common::vectorize<int64_t>(dst_tensor->dims()),
|
|
common::vectorize<int64_t>(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<phi::distributed::DistTensor*>(value.impl().get());
|
|
if (self->tensor.is_dist_tensor()) {
|
|
auto* dst_tensor =
|
|
static_cast<phi::distributed::DistTensor*>(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<phi::SelectedRows*>(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<phi::SelectedRows*>(self->tensor.impl().get());
|
|
|
|
PADDLE_ENFORCE(selected_rows->has_allocation(),
|
|
common::errors::Fatal("SelectedRows must be has_allocation."));
|
|
|
|
auto* dense_tensor =
|
|
static_cast<phi::DenseTensor*>(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<DenseTensor>(*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<int64_t> slice_starts, slice_ends, slice_strides;
|
|
std::vector<int64_t> slice_axes, decrease_axis, infer_flags, none_axes;
|
|
|
|
bool has_advanced_index = false;
|
|
bool use_strided_slice = false;
|
|
std::vector<int> 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<Tensor> 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<Tensor> transed_index;
|
|
std::vector<int> 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<phi::SelectedRows*>(self->tensor.impl().get());
|
|
ptr = static_cast<phi::DenseTensor*>(selected_rows->mutable_value());
|
|
} else if (self->tensor.is_dist_tensor()) {
|
|
#ifdef PADDLE_WITH_DISTRIBUTE
|
|
auto* dist_tensor =
|
|
static_cast<phi::distributed::DistTensor*>(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<phi::DenseTensor*>(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<size_t> dims(tensor_dims.size());
|
|
std::vector<size_t> stride = common::vectorize<size_t>(tensor.strides());
|
|
|
|
size_t numel = 1;
|
|
for (int i = tensor_dims.size() - 1; i >= 0; --i) {
|
|
dims[i] = static_cast<size_t>(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<T>(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<void*>(pybind11::detail::array_proxy(array)->data), \
|
|
static_cast<void*>(&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<int64_t> slice_starts, slice_ends, slice_strides;
|
|
std::vector<int64_t> slice_axes, decrease_axis, infer_flags, none_axes;
|
|
|
|
bool has_advanced_index = false;
|
|
bool use_strided_slice = false;
|
|
std::vector<int> 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<Tensor> 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<phi::Scalar> 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<std::vector<Tensor>, 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<Tensor> transed_index;
|
|
std::vector<int> 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<SetValueWithTensorGradNode> 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<DenseTensor>(
|
|
transback_sub_tensor.impl())
|
|
->meta()
|
|
.is_contiguous())
|
|
? Tensor(std::make_shared<DenseTensor>(
|
|
paddle::experimental::Trans2Contiguous(
|
|
*(std::dynamic_pointer_cast<DenseTensor>(
|
|
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<SetValueWithTensorGradNode>(
|
|
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<PyObject*>(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<egr::GradNodeAccumulation>(self->tensor));
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<egr::GradNodeBase> 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<egr::GradNodeAccumulation>(grad_node);
|
|
hook_id = accumulation_grad_node->RegisterGradientHook(
|
|
rank_info.first,
|
|
rank_info.second,
|
|
std::make_shared<PyTensorHook>(hook_func));
|
|
|
|
} else {
|
|
VLOG(6) << "Register hook for non leaf tensor: " << self->tensor.name();
|
|
std::shared_ptr<egr::GradNodeBase> 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<PyTensorHook>(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<egr::GradNodeBase> 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<egr::GradNodeBase> 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<egr::GradNodeAccumulation>(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<PyObject*>(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<egr::GradNodeBase> 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<egr::GradNodeAccumulation>(grad_node);
|
|
accumulation_grad_node->RegisterReduceHook(
|
|
std::make_shared<PyVoidHook>(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<DenseTensor>());
|
|
} else if (var_type == framework::proto::VarType::SELECTED_ROWS) {
|
|
grad_tensor->set_impl(std::make_shared<phi::SelectedRows>());
|
|
}
|
|
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<phi::DenseTensor*>(self->tensor.impl().get());
|
|
if (dense_tensor != nullptr && dense_tensor->Holder() != nullptr) {
|
|
DenseTensor tmp(std::make_shared<phi::Allocation>(
|
|
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<phi::DenseTensor*>(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<phi::distributed::DistTensor*>(self->tensor.impl().get())
|
|
->unsafe_mutable_value();
|
|
} else {
|
|
dense_tensor = static_cast<phi::DenseTensor*>(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<phi::distributed::DistTensor*>(self->tensor.impl().get());
|
|
auto target_dist_tensor = std::make_shared<phi::distributed::DistTensor>(
|
|
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<DenseTensor>(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<egr::VariableCompatTensor>();
|
|
*var_tensor->GetMutable<Vocab>() = 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<egr::VariableCompatTensor>();
|
|
*var_tensor->GetMutable<Strings>() = 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<const egr::VariableCompatTensor*>(self->tensor.impl().get());
|
|
return ToPyObject(var_tensor->Get<Vocab>());
|
|
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<phi::SparseCooTensor>(self->tensor.impl());
|
|
return ToPyObject(sparse_coo_tensor->nnz());
|
|
} else {
|
|
auto sparse_csr_tensor =
|
|
std::dynamic_pointer_cast<phi::SparseCsrTensor>(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<phi::SparseCooTensor>(self->tensor.impl());
|
|
Tensor tensor(
|
|
std::make_shared<DenseTensor>(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<phi::SparseCooTensor>(self->tensor.impl());
|
|
Tensor tensor(
|
|
std::make_shared<DenseTensor>(sparse_coo_tensor->non_zero_elements()));
|
|
return ToPyObject(tensor);
|
|
} else {
|
|
auto sparse_csr_tensor =
|
|
std::dynamic_pointer_cast<phi::SparseCsrTensor>(self->tensor.impl());
|
|
Tensor tensor(
|
|
std::make_shared<DenseTensor>(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<phi::SparseCsrTensor>(self->tensor.impl());
|
|
Tensor tensor(
|
|
std::make_shared<DenseTensor>(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<phi::SparseCsrTensor>(self->tensor.impl());
|
|
Tensor tensor(
|
|
std::make_shared<DenseTensor>(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<PyObject*>(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<phi::SparseCooTensor>(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<phi::SelectedRows>(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<DenseTensor>(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<phi::distributed::DistTensor*>(self->tensor.impl().get())
|
|
->unsafe_mutable_value();
|
|
} else {
|
|
dense_tensor = static_cast<phi::DenseTensor*>(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<phi::DenseTensor*>(grad->impl().get());
|
|
return ToPyObject(grad_tensor);
|
|
} else if (grad->is_dist_tensor()) {
|
|
auto* grad_tensor =
|
|
static_cast<phi::distributed::DistTensor*>(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<phi::distributed::DistTensor*>(self->tensor.impl().get());
|
|
Tensor result(std::make_shared<DenseTensor>(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::GradNodeAccumulation>(
|
|
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<DenseTensor>( // 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<phi::distributed::DistTensor>( // 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<int64_t> 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<int>(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<char*>("dim"), nullptr};
|
|
PyObject* dim_obj = nullptr;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &dim_obj)) {
|
|
RETURN_PY_NONE
|
|
}
|
|
|
|
std::vector<int64_t> 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<int>(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<int>(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<int>(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<phi::distributed::DistTensor*>(self->tensor.impl().get())
|
|
->unsafe_mutable_value();
|
|
} else {
|
|
dense_tensor = static_cast<phi::DenseTensor*>(self->tensor.impl().get());
|
|
}
|
|
if (dense_tensor->meta().is_contiguous()) {
|
|
Py_INCREF(self);
|
|
return reinterpret_cast<PyObject*>(self);
|
|
} else {
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
*dense_tensor = paddle::experimental::Trans2Contiguous(*dense_tensor);
|
|
Py_INCREF(self);
|
|
return reinterpret_cast<PyObject*>(self);
|
|
}
|
|
} else {
|
|
Py_INCREF(self);
|
|
return reinterpret_cast<PyObject*>(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<DenseTensor>(self->tensor.impl());
|
|
return ToPyObject(dense_tensor->meta().is_contiguous());
|
|
} else if (self->tensor.is_dist_tensor()) {
|
|
auto dense_tensor = std::dynamic_pointer_cast<phi::distributed::DistTensor>(
|
|
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<phi::SparseCooTensor>(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<phi::SparseCsrTensor>(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<phi::SparseCooTensor>(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<phi::SparseCsrTensor>(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<phi::DenseTensor*>(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<phi::distributed::DistTensor*>(self->tensor.impl().get())
|
|
->unsafe_mutable_value();
|
|
} else {
|
|
dense_tensor = static_cast<phi::DenseTensor*>(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<phi::DenseTensor*>(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::XPUContext*>(
|
|
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<phi::StringTensor>(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
|