eager_method.cc 17.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* 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
#include <Python.h>

#include <string>
#include <vector>

#include "pybind11/numpy.h"
#include "pybind11/pybind11.h"

20
#include "paddle/fluid/eager/accumulation/accumulation_node.h"
21 22
#include "paddle/fluid/eager/api/all.h"
#include "paddle/fluid/eager/autograd_meta.h"
23
#include "paddle/fluid/eager/utils.h"
24 25 26 27 28 29
#include "paddle/fluid/memory/allocation/allocator.h"
#include "paddle/fluid/memory/memcpy.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"
30
#include "paddle/pten/api/include/api.h"
31
#include "paddle/pten/common/data_type.h"
32
#include "paddle/pten/core/compat/convert_utils.h"
33 34 35 36
#include "paddle/pten/core/dense_tensor.h"
namespace paddle {
namespace pybind {

37
extern void InitEagerTensorWithNumpyValue(TensorObject* self,
38 39 40
                                          const pybind11::object& array,
                                          bool zero_copy);

41
extern PyTypeObject* p_tensor_type;
42

43 44
static PyObject* eager_tensor_method_numpy(TensorObject* self, PyObject* args,
                                           PyObject* kwargs) {
J
Jiabin Yang 已提交
45
  EAGER_SYNC_TRY
46
  PADDLE_ENFORCE_EQ(
47
      self->tensor.initialized(), true,
48 49 50
      platform::errors::InvalidArgument(
          "Tensor data of %s is Empty that indicates we have null tensor for "
          "now, please check if it has no data and initialize it first.",
51 52 53 54
          self->tensor.name()));
  auto tensor_dims = self->tensor.shape();
  auto numpy_dtype = TensorDtype2NumpyDtype(self->tensor.type());
  auto sizeof_dtype = pten::DataTypeSize(self->tensor.type());
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
  Py_intptr_t py_dims[paddle::framework::DDim::kMaxRank];
  Py_intptr_t py_strides[paddle::framework::DDim::kMaxRank];
  size_t numel = 1;
  for (int i = tensor_dims.size() - 1; i >= 0; --i) {
    py_dims[i] = static_cast<size_t>(tensor_dims[i]);
    py_strides[i] = sizeof_dtype * numel;
    numel *= py_dims[i];
  }
  auto& api = pybind11::detail::npy_api::get();
  PyObject* array = api.PyArray_NewFromDescr_(
      api.PyArray_Type_, api.PyArray_DescrFromType_(numpy_dtype),
      tensor_dims.size(), py_dims, py_strides, nullptr,
      pybind11::detail::npy_api::NPY_ARRAY_ALIGNED_ |
          pybind11::detail::npy_api::NPY_ARRAY_WRITEABLE_,
      nullptr);

71
  if (self->tensor.is_cpu()) {
72
    auto dense_tensor =
73
        std::dynamic_pointer_cast<pten::DenseTensor>(self->tensor.impl());
74 75 76 77 78 79
    platform::CPUPlace place;
    // deep copy
    paddle::memory::Copy(place, reinterpret_cast<void*>(
                                    pybind11::detail::array_proxy(array)->data),
                         place, dense_tensor->data(), sizeof_dtype * numel);
#if defined(PADDLE_WITH_CUDA)
80
  } else if (self->tensor.is_cuda()) {
81
    auto dense_tensor =
82
        std::dynamic_pointer_cast<pten::DenseTensor>(self->tensor.impl());
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

    paddle::platform::GpuMemcpySync(
        pybind11::detail::array_proxy(array)->data, dense_tensor->data(),
        pten::DataTypeSize(dense_tensor->dtype()) * dense_tensor->numel(),
        cudaMemcpyDeviceToHost);
#endif
  } else {
    PADDLE_THROW(platform::errors::InvalidArgument(
        "Tensor.numpy() only support cpu tensor."));
    Py_INCREF(Py_None);
    return Py_None;
  }

  return array;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

100
static PyObject* eager_tensor_method__is_initialized(TensorObject* self,
101 102
                                                     PyObject* args,
                                                     PyObject* kwargs) {
J
Jiabin Yang 已提交
103
  EAGER_SYNC_TRY
104
  return ToPyObject(self->tensor.initialized());
105 106 107
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

108
static PyObject* eager_tensor_method__copy_to(TensorObject* self,
109 110 111 112 113 114
                                              PyObject* args,
                                              PyObject* kwargs) {
  EAGER_SYNC_TRY
  bool blocking = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 0), 0);
  auto place = CastPyArg2Place(PyTuple_GET_ITEM(args, 1), 1);
  auto cp_tensor =
115
      self->tensor.copy_to(pten::TransToPtenBackend(place), blocking);
116 117 118
  egr::EagerUtils::autograd_meta(&cp_tensor)->SetStopGradient(true);
  egr::EagerUtils::autograd_meta(&cp_tensor)
      ->SetPersistable(
119
          egr::EagerUtils::autograd_meta(&(self->tensor))->Persistable());
120 121 122 123
  return ToPyObject(cp_tensor);
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

124
static PyObject* eager_tensor_method_reconstruct_from_(TensorObject* self,
125 126 127
                                                       PyObject* args,
                                                       PyObject* kwargs) {
  EAGER_SYNC_TRY
128 129 130
  paddle::experimental::Tensor src_tensor =
      CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0);
  std::string orig_name = self->tensor.name();
131 132
  VLOG(6) << "Start Reconstructing Tensor from" << src_tensor.name() << " to "
          << orig_name;
133
  self->tensor = src_tensor;
134 135

  // Recover source name
136
  self->tensor.set_name(orig_name);
137 138

  VLOG(6) << "Finished Reconstructing Tensor from" << src_tensor.name()
139
          << " to " << self->tensor.name();
140 141 142 143 144
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

145 146
static PyObject* eager_tensor_method_copy_(TensorObject* self, PyObject* args,
                                           PyObject* kwargs) {
147
  EAGER_SYNC_TRY
148 149
  paddle::experimental::Tensor src_tensor =
      CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0);
150
  bool blocking = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 1), 1);
151
  VLOG(6) << "Start Copy Tensor " << src_tensor.name() << " to "
152 153 154
          << self->tensor.name();
  if (!self->tensor.defined()) {
    egr::EagerUtils::autograd_meta(&(self->tensor))
155 156
        ->SetStopGradient(
            egr::EagerUtils::autograd_meta(&(src_tensor))->StopGradient());
157
    egr::EagerUtils::autograd_meta(&(self->tensor))
158 159 160 161
        ->SetPersistable(
            egr::EagerUtils::autograd_meta(&(src_tensor))->Persistable());
  }

162
  self->tensor.copy_(src_tensor, blocking);
163

164
  VLOG(6) << "Finish Copy Tensor " << src_tensor.name() << " to "
165
          << self->tensor.name();
166 167 168 169 170
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

171 172
static PyObject* eager_tensor_retain_grads(TensorObject* self, PyObject* args,
                                           PyObject* kwargs) {
173
  EAGER_TRY
174
  if (egr::Controller::Instance().HasGrad()) {
175
    auto meta = egr::EagerUtils::autograd_meta(&(self->tensor));
176
    if (!meta->GetMutableGradNode()) {
177
      VLOG(6) << "Make grad node of tensor: " << self->tensor.name()
178 179 180
              << "become accumulation node";
      meta->SetGradNode(std::make_shared<egr::GradNodeAccumulation>());
    }
181
    egr::egr_utils_api::RetainGradForTensor(self->tensor);
182
  }
183 184 185 186 187
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

188
static PyObject* eager_tensor__clear_gradient(TensorObject* self,
189 190 191
                                              PyObject* args,
                                              PyObject* kwargs) {
  EAGER_SYNC_TRY
192
  VLOG(4) << "ClearGradient " << self->tensor.name();
193

194 195
  paddle::experimental::Tensor* grad;
  if (egr::egr_utils_api::IsLeafTensor(self->tensor)) {
196 197
    // Add RetainGrad as PostHook to AccumulationNode
    std::shared_ptr<egr::GradNodeBase> grad_node =
198
        egr::EagerUtils::grad_node(self->tensor);
199 200 201 202 203 204 205 206 207
    PADDLE_ENFORCE(
        grad_node.get() != nullptr,
        paddle::platform::errors::Fatal("Detected NULL grad_node"
                                        "Leaf tensor should have had grad_node "
                                        "with type: GradNodeAccumulation"));
    auto accumulation_grad_node =
        std::dynamic_pointer_cast<egr::GradNodeAccumulation>(grad_node);
    grad = accumulation_grad_node->Grad();
  } else {
208
    auto meta = egr::EagerUtils::unsafe_autograd_meta(self->tensor);
209
    grad = meta->MutableGrad();
210 211
  }

212
  if (grad->initialized()) {
213
    VLOG(4) << "Gradient of " << self->tensor.name()
214 215
            << " is initialized, will be released.";
    auto dense_tensor =
216
        std::dynamic_pointer_cast<pten::DenseTensor>(grad->impl());
217
    dense_tensor->MoveMemoryHolder();
218 219 220 221 222 223
  }
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

224 225
static PyObject* eager_tensor__zero_grads(TensorObject* self, PyObject* args,
                                          PyObject* kwargs) {
226
  EAGER_TRY
227
  VLOG(4) << "ZeroGrads " << self->tensor.name();
228

229
  if (egr::egr_utils_api::IsLeafTensor(self->tensor)) {
230 231
    // Add RetainGrad as PostHook to AccumulationNode
    std::shared_ptr<egr::GradNodeBase> grad_node =
232
        egr::EagerUtils::grad_node(self->tensor);
233 234 235 236 237 238 239
    PADDLE_ENFORCE(
        grad_node.get() != nullptr,
        paddle::platform::errors::Fatal("Detected NULL grad_node"
                                        "Leaf tensor should have had grad_node "
                                        "with type: GradNodeAccumulation"));
    auto accumulation_grad_node =
        std::dynamic_pointer_cast<egr::GradNodeAccumulation>(grad_node);
240
    if (accumulation_grad_node->Grad()->initialized()) {
241 242 243
      accumulation_grad_node->Grad()->set_impl(
          paddle::experimental::zeros_like(*(accumulation_grad_node->Grad()))
              .impl());
244
    }
245
  } else {
246
    auto meta = egr::EagerUtils::unsafe_autograd_meta(self->tensor);
247
    if (meta->MutableGrad()->initialized()) {
248 249
      meta->MutableGrad()->set_impl(
          paddle::experimental::zeros_like(*(meta->MutableGrad())).impl());
250
    }
251 252 253 254 255 256 257
  }

  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

258
static PyObject* eager_tensor__share_buffer_to(TensorObject* self,
259 260 261
                                               PyObject* args,
                                               PyObject* kwargs) {
  EAGER_SYNC_TRY
262 263 264
  paddle::experimental::Tensor* dst_ptr =
      &(reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor);
  PADDLE_ENFORCE_EQ(self->tensor.initialized(), true,
265 266 267
                    platform::errors::InvalidArgument(
                        "Tensor %s has not been initialized! please initialize "
                        "src tensor before share_buffer_with to other.",
268
                        self->tensor.name()));
269
  auto* src_tensor =
270
      static_cast<paddle::framework::Tensor*>(self->tensor.impl().get());
271 272 273 274
  auto dst_tensor =
      static_cast<paddle::framework::Tensor*>(dst_ptr->impl().get());
  dst_tensor->ShareDataWith(*src_tensor);
  dst_tensor->ShareDataTypeWith(*src_tensor);
275 276 277 278 279
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

280
static PyObject* eager_tensor__is_shared_buffer_with(TensorObject* self,
281 282 283
                                                     PyObject* args,
                                                     PyObject* kwargs) {
  EAGER_SYNC_TRY
284 285 286
  paddle::experimental::Tensor* dst_ptr =
      &(reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor);
  PADDLE_ENFORCE_EQ(self->tensor.initialized(), true,
287 288 289
                    platform::errors::InvalidArgument(
                        "Tensor %s has not been initialized! please initialize "
                        "src tensor before share_buffer_with to other.",
290
                        self->tensor.name()));
291
  bool res = false;
292
  if (!self->tensor.defined() || !dst_ptr->defined()) {
293 294 295
    return ToPyObject(res);
  }
  auto* self_ptr =
296
      static_cast<paddle::framework::Tensor*>(self->tensor.impl().get());
297 298 299 300 301 302 303
  auto dst_tensor =
      static_cast<paddle::framework::Tensor*>(dst_ptr->impl().get());
  res = dst_tensor->IsSharedBufferWith(*self_ptr);
  return ToPyObject(res);
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

304 305 306
static PyObject* eager_tensor__share_underline_tensor_to(TensorObject* self,
                                                         PyObject* args,
                                                         PyObject* kwargs) {
307
  EAGER_SYNC_TRY
308 309 310
  paddle::experimental::Tensor* src_ptr =
      &(reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor);
  PADDLE_ENFORCE_EQ(self->tensor.initialized(), true,
311 312 313
                    platform::errors::InvalidArgument(
                        "Tensor %s has not been initialized! please initialize "
                        "src tensor before share_buffer_with to other.",
314 315
                        self->tensor.name()));
  src_ptr->set_impl(self->tensor.impl());
316 317 318 319 320 321
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

static PyObject* eager_tensor__is_shared_underline_tensor_with(
322
    TensorObject* self, PyObject* args, PyObject* kwargs) {
323
  EAGER_SYNC_TRY
324 325
  paddle::experimental::Tensor src_tensor =
      CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0);
326 327 328 329 330 331
  PADDLE_ENFORCE_EQ(src_tensor.initialized(), true,
                    platform::errors::InvalidArgument(
                        "Tensor %s has not been initialized! please initialize "
                        "src tensor before share_buffer_with to other.",
                        src_tensor.name()));
  bool res = false;
332
  if (!self->tensor.defined() || !src_tensor.defined()) {
333 334
    return ToPyObject(res);
  }
335
  res = (self->tensor.impl().get() == src_tensor.impl().get());
336 337 338 339
  return ToPyObject(res);
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

340 341
static PyObject* eager_tensor_method_detach(TensorObject* self, PyObject* args,
                                            PyObject* kwargs) {
342 343
  EAGER_SYNC_TRY
  PADDLE_ENFORCE_EQ(
344
      self->tensor.initialized(), true,
345
      platform::errors::InvalidArgument("Tensor %s has not been initialized!",
346
                                        self->tensor.name()));
347

348
  PyObject* obj = p_tensor_type->tp_alloc(p_tensor_type, 0);
349
  if (obj) {
350 351 352 353 354 355
    auto v = reinterpret_cast<TensorObject*>(obj);
    new (&(v->tensor)) paddle::experimental::Tensor();
    v->tensor.set_impl(self->tensor.impl());
    v->tensor.set_name(egr::Controller::Instance().GenerateUniqueName());
    auto autograd_meta_src = egr::EagerUtils::autograd_meta(&(self->tensor));
    auto autograd_meta = egr::EagerUtils::autograd_meta(&(v->tensor));
356 357 358 359 360 361 362 363 364 365
    autograd_meta->SetPersistable(autograd_meta_src->Persistable());
  } else {
    PADDLE_THROW(platform::errors::Fatal(
        "tp_alloc return null, can not new a PyObject."));
  }

  return obj;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

366 367 368
static PyObject* eager_tensor_method_get_underline_tensor(TensorObject* self,
                                                          PyObject* args,
                                                          PyObject* kwargs) {
369
  EAGER_SYNC_TRY
370 371 372
  if (self->tensor.is_dense_tensor()) {
    auto* tensor =
        static_cast<paddle::framework::LoDTensor*>(self->tensor.impl().get());
373 374 375 376 377 378 379 380 381
    VLOG(6) << "tensor: " << tensor->IsInitialized();
    return ToPyObject(tensor);
  } else {
    Py_IncRef(Py_None);
    return Py_None;
  }
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

382
// NOTE(wuweilong): Set value and not change self's original place
383
static PyObject* eager_tensor_method_set_value(TensorObject* self,
384 385 386
                                               PyObject* args,
                                               PyObject* kwargs) {
  EAGER_TRY
387
  VLOG(4) << "Value " << self->tensor.name();
388 389 390 391 392 393 394 395
  pybind11::object numpy_value =
      pybind11::object(pybind11::handle(PyTuple_GET_ITEM(args, 0)), true);
  InitEagerTensorWithNumpyValue(self, numpy_value, false);
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

396 397 398 399
PyMethodDef variable_methods[] = {
    {"numpy", (PyCFunction)(void (*)(void))eager_tensor_method_numpy,
     METH_VARARGS | METH_KEYWORDS, NULL},
    {"_is_initialized",
400 401 402 403 404
     (PyCFunction)(void (*)(void))eager_tensor_method__is_initialized,
     METH_VARARGS | METH_KEYWORDS, NULL},
    {"_copy_to", (PyCFunction)(void (*)(void))eager_tensor_method__copy_to,
     METH_VARARGS | METH_KEYWORDS, NULL},
    {"copy_", (PyCFunction)(void (*)(void))eager_tensor_method_copy_,
405
     METH_VARARGS | METH_KEYWORDS, NULL},
406 407 408
    {"reconstruct_from_",
     (PyCFunction)(void (*)(void))eager_tensor_method_reconstruct_from_,
     METH_VARARGS | METH_KEYWORDS, NULL},
409 410
    {"retain_grads", (PyCFunction)(void (*)(void))eager_tensor_retain_grads,
     METH_VARARGS | METH_KEYWORDS, NULL},
411 412 413 414 415
    {"_clear_gradient",
     (PyCFunction)(void (*)(void))eager_tensor__clear_gradient,
     METH_VARARGS | METH_KEYWORDS, NULL},
    {"_zero_grads", (PyCFunction)(void (*)(void))eager_tensor__zero_grads,
     METH_VARARGS | METH_KEYWORDS, NULL},
416
    {"_share_buffer_to",
417 418
     (PyCFunction)(void (*)(void))eager_tensor__share_buffer_to,
     METH_VARARGS | METH_KEYWORDS, NULL},
419
    {"_is_shared_buffer_with",
420 421
     (PyCFunction)(void (*)(void))eager_tensor__is_shared_buffer_with,
     METH_VARARGS | METH_KEYWORDS, NULL},
422 423 424 425 426 427
    {"_share_underline_tensor_to",
     (PyCFunction)(void (*)(void))eager_tensor__share_underline_tensor_to,
     METH_VARARGS | METH_KEYWORDS, NULL},
    {"_is_shared_underline_tensor_with",
     (PyCFunction)(void (*)(void))eager_tensor__is_shared_underline_tensor_with,
     METH_VARARGS | METH_KEYWORDS, NULL},
428 429
    {"detach", (PyCFunction)(void (*)(void))eager_tensor_method_detach,
     METH_VARARGS | METH_KEYWORDS, NULL},
430 431 432
    {"get_tensor",
     (PyCFunction)(void (*)(void))eager_tensor_method_get_underline_tensor,
     METH_VARARGS | METH_KEYWORDS, NULL},
433 434
    {"_set_value", (PyCFunction)(void (*)(void))eager_tensor_method_set_value,
     METH_VARARGS | METH_KEYWORDS, NULL},
435 436 437 438
    {NULL, NULL, 0, NULL}};

}  // namespace pybind
}  // namespace paddle