eager_method.cc 17.7 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
#include "paddle/fluid/framework/convert_utils.h"
25 26 27 28 29 30
#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"
31
#include "paddle/pten/api/include/api.h"
32
#include "paddle/pten/common/data_type.h"
33
#include "paddle/pten/core/compat/convert_utils.h"
34 35 36 37
#include "paddle/pten/core/dense_tensor.h"
namespace paddle {
namespace pybind {

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

42
extern PyTypeObject* p_tensor_type;
43

44 45
static PyObject* eager_tensor_method_numpy(TensorObject* self, PyObject* args,
                                           PyObject* kwargs) {
J
Jiabin Yang 已提交
46
  EAGER_SYNC_TRY
47
  PADDLE_ENFORCE_EQ(
48
      self->tensor.initialized(), true,
49 50 51
      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.",
52 53 54
          self->tensor.name()));
  auto tensor_dims = self->tensor.shape();
  auto numpy_dtype = TensorDtype2NumpyDtype(self->tensor.type());
55
  auto sizeof_dtype = paddle::framework::DataTypeSize(self->tensor.type());
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
  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);

72
  if (self->tensor.is_cpu()) {
73
    auto dense_tensor =
74
        std::dynamic_pointer_cast<pten::DenseTensor>(self->tensor.impl());
75 76 77 78 79 80
    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)
81
  } else if (self->tensor.is_cuda()) {
82
    auto dense_tensor =
83
        std::dynamic_pointer_cast<pten::DenseTensor>(self->tensor.impl());
84 85 86

    paddle::platform::GpuMemcpySync(
        pybind11::detail::array_proxy(array)->data, dense_tensor->data(),
87 88
        paddle::framework::DataTypeSize(dense_tensor->dtype()) *
            dense_tensor->numel(),
89 90 91 92 93 94 95 96 97 98 99 100 101
        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
}

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

110
static PyObject* eager_tensor_method__copy_to(TensorObject* self,
111 112 113 114 115 116
                                              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 =
117
      self->tensor.copy_to(pten::TransToPtenBackend(place), blocking);
118 119 120
  egr::EagerUtils::autograd_meta(&cp_tensor)->SetStopGradient(true);
  egr::EagerUtils::autograd_meta(&cp_tensor)
      ->SetPersistable(
121
          egr::EagerUtils::autograd_meta(&(self->tensor))->Persistable());
122 123 124 125
  return ToPyObject(cp_tensor);
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

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

  // Recover source name
138
  self->tensor.set_name(orig_name);
139 140

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

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

164
  self->tensor.copy_(src_tensor, blocking);
165

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

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

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

196 197
  paddle::experimental::Tensor* grad;
  if (egr::egr_utils_api::IsLeafTensor(self->tensor)) {
198 199
    // Add RetainGrad as PostHook to AccumulationNode
    std::shared_ptr<egr::GradNodeBase> grad_node =
200
        egr::EagerUtils::grad_node(self->tensor);
201 202 203 204 205 206 207 208 209
    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 {
210
    auto meta = egr::EagerUtils::unsafe_autograd_meta(self->tensor);
211
    grad = meta->MutableGrad();
212 213
  }

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

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

231
  if (egr::egr_utils_api::IsLeafTensor(self->tensor)) {
232 233
    // Add RetainGrad as PostHook to AccumulationNode
    std::shared_ptr<egr::GradNodeBase> grad_node =
234
        egr::EagerUtils::grad_node(self->tensor);
235 236 237 238 239 240 241
    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);
242
    if (accumulation_grad_node->Grad()->initialized()) {
243 244 245
      accumulation_grad_node->Grad()->set_impl(
          paddle::experimental::zeros_like(*(accumulation_grad_node->Grad()))
              .impl());
246
    }
247
  } else {
248
    auto meta = egr::EagerUtils::unsafe_autograd_meta(self->tensor);
249
    if (meta->MutableGrad()->initialized()) {
250 251
      meta->MutableGrad()->set_impl(
          paddle::experimental::zeros_like(*(meta->MutableGrad())).impl());
252
    }
253 254 255 256 257 258 259
  }

  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

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

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

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

static PyObject* eager_tensor__is_shared_underline_tensor_with(
324
    TensorObject* self, PyObject* args, PyObject* kwargs) {
325
  EAGER_SYNC_TRY
326 327
  paddle::experimental::Tensor src_tensor =
      CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0);
328 329 330 331 332 333
  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;
334
  if (!self->tensor.defined() || !src_tensor.defined()) {
335 336
    return ToPyObject(res);
  }
337
  res = (self->tensor.impl().get() == src_tensor.impl().get());
338 339 340 341
  return ToPyObject(res);
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

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

350
  PyObject* obj = p_tensor_type->tp_alloc(p_tensor_type, 0);
351
  if (obj) {
352 353 354 355 356 357
    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));
358 359 360 361 362 363 364 365 366 367
    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
}

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

384
// NOTE(wuweilong): Set value and not change self's original place
385
static PyObject* eager_tensor_method_set_value(TensorObject* self,
386 387 388
                                               PyObject* args,
                                               PyObject* kwargs) {
  EAGER_TRY
389
  VLOG(4) << "Value " << self->tensor.name();
390 391 392 393 394 395 396 397
  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
}

398 399 400 401
PyMethodDef variable_methods[] = {
    {"numpy", (PyCFunction)(void (*)(void))eager_tensor_method_numpy,
     METH_VARARGS | METH_KEYWORDS, NULL},
    {"_is_initialized",
402 403 404 405 406
     (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_,
407
     METH_VARARGS | METH_KEYWORDS, NULL},
408 409 410
    {"reconstruct_from_",
     (PyCFunction)(void (*)(void))eager_tensor_method_reconstruct_from_,
     METH_VARARGS | METH_KEYWORDS, NULL},
411 412
    {"retain_grads", (PyCFunction)(void (*)(void))eager_tensor_retain_grads,
     METH_VARARGS | METH_KEYWORDS, NULL},
413 414 415 416 417
    {"_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},
418
    {"_share_buffer_to",
419 420
     (PyCFunction)(void (*)(void))eager_tensor__share_buffer_to,
     METH_VARARGS | METH_KEYWORDS, NULL},
421
    {"_is_shared_buffer_with",
422 423
     (PyCFunction)(void (*)(void))eager_tensor__is_shared_buffer_with,
     METH_VARARGS | METH_KEYWORDS, NULL},
424 425 426 427 428 429
    {"_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},
430 431
    {"detach", (PyCFunction)(void (*)(void))eager_tensor_method_detach,
     METH_VARARGS | METH_KEYWORDS, NULL},
432 433 434
    {"get_tensor",
     (PyCFunction)(void (*)(void))eager_tensor_method_get_underline_tensor,
     METH_VARARGS | METH_KEYWORDS, NULL},
435 436
    {"_set_value", (PyCFunction)(void (*)(void))eager_tensor_method_set_value,
     METH_VARARGS | METH_KEYWORDS, NULL},
437 438 439 440
    {NULL, NULL, 0, NULL}};

}  // namespace pybind
}  // namespace paddle