eager_method.cc 17.8 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 32 33 34
#include "paddle/phi/api/include/api.h"
#include "paddle/phi/common/data_type.h"
#include "paddle/phi/core/compat/convert_utils.h"
#include "paddle/phi/core/dense_tensor.h"
35 36 37
namespace paddle {
namespace pybind {

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

42
extern PyTypeObject* p_tensor_type;
43

44 45 46
static PyObject* tensor_method_numpy(TensorObject* self, PyObject* args,
                                     PyObject* kwargs) {
  EAGER_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<phi::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<phi::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 103 104 105
static PyObject* tensor_method__is_initialized(TensorObject* self,
                                               PyObject* args,
                                               PyObject* kwargs) {
  EAGER_TRY
106
  return ToPyObject(self->tensor.initialized());
107 108 109
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

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

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

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

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

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

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

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

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

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

194 195 196 197 198 199
  Py_ssize_t args_num = PyTuple_Size(args);
  bool set_to_zero = true;
  if (args_num == (Py_ssize_t)1) {
    CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 0), 0);
  }

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

218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
  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()) {
    if (grad->initialized()) {
      if (set_to_zero) {
        grad->set_impl(paddle::experimental::zeros_like(*grad).impl());
      } else {
        VLOG(4) << "Gradient of " << self->tensor.name()
                << " is initialized, will be released.";
        auto dense_tensor =
            std::dynamic_pointer_cast<phi::DenseTensor>(grad->impl());
        dense_tensor->MoveMemoryHolder();
      }
    }
237
  }
238

239 240 241 242 243
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

244 245
static PyObject* tensor__zero_grads(TensorObject* self, PyObject* args,
                                    PyObject* kwargs) {
246
  EAGER_TRY
247
  VLOG(4) << "ZeroGrads " << self->tensor.name();
248

249
  if (egr::egr_utils_api::IsLeafTensor(self->tensor)) {
250 251
    // Add RetainGrad as PostHook to AccumulationNode
    std::shared_ptr<egr::GradNodeBase> grad_node =
252
        egr::EagerUtils::grad_node(self->tensor);
253 254 255 256 257 258 259
    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);
260
    if (accumulation_grad_node->Grad()->initialized()) {
261 262 263
      accumulation_grad_node->Grad()->set_impl(
          paddle::experimental::zeros_like(*(accumulation_grad_node->Grad()))
              .impl());
264
    }
265
  } else {
266
    auto meta = egr::EagerUtils::unsafe_autograd_meta(self->tensor);
267
    if (meta->MutableGrad()->initialized()) {
268 269
      meta->MutableGrad()->set_impl(
          paddle::experimental::zeros_like(*(meta->MutableGrad())).impl());
270
    }
271 272 273 274 275 276 277
  }

  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

278 279 280
static PyObject* tensor__share_buffer_to(TensorObject* self, PyObject* args,
                                         PyObject* kwargs) {
  EAGER_TRY
281 282 283
  paddle::experimental::Tensor* dst_ptr =
      &(reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor);
  PADDLE_ENFORCE_EQ(self->tensor.initialized(), true,
284 285 286
                    platform::errors::InvalidArgument(
                        "Tensor %s has not been initialized! please initialize "
                        "src tensor before share_buffer_with to other.",
287
                        self->tensor.name()));
288
  auto* src_tensor =
289
      static_cast<paddle::framework::Tensor*>(self->tensor.impl().get());
290 291 292 293
  auto dst_tensor =
      static_cast<paddle::framework::Tensor*>(dst_ptr->impl().get());
  dst_tensor->ShareDataWith(*src_tensor);
  dst_tensor->ShareDataTypeWith(*src_tensor);
294 295 296 297 298
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

299 300 301 302
static PyObject* tensor__is_shared_buffer_with(TensorObject* self,
                                               PyObject* args,
                                               PyObject* kwargs) {
  EAGER_TRY
303 304 305
  paddle::experimental::Tensor* dst_ptr =
      &(reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor);
  PADDLE_ENFORCE_EQ(self->tensor.initialized(), true,
306 307 308
                    platform::errors::InvalidArgument(
                        "Tensor %s has not been initialized! please initialize "
                        "src tensor before share_buffer_with to other.",
309
                        self->tensor.name()));
310
  bool res = false;
311
  if (!self->tensor.defined() || !dst_ptr->defined()) {
312 313 314
    return ToPyObject(res);
  }
  auto* self_ptr =
315
      static_cast<paddle::framework::Tensor*>(self->tensor.impl().get());
316 317 318 319 320 321 322
  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
}

323 324 325 326
static PyObject* tensor__share_underline_tensor_to(TensorObject* self,
                                                   PyObject* args,
                                                   PyObject* kwargs) {
  EAGER_TRY
327 328 329
  paddle::experimental::Tensor* src_ptr =
      &(reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor);
  PADDLE_ENFORCE_EQ(self->tensor.initialized(), true,
330 331 332
                    platform::errors::InvalidArgument(
                        "Tensor %s has not been initialized! please initialize "
                        "src tensor before share_buffer_with to other.",
333 334
                        self->tensor.name()));
  src_ptr->set_impl(self->tensor.impl());
335 336 337 338 339
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

340 341 342 343
static PyObject* tensor__is_shared_underline_tensor_with(TensorObject* self,
                                                         PyObject* args,
                                                         PyObject* kwargs) {
  EAGER_TRY
344 345
  paddle::experimental::Tensor src_tensor =
      CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0);
346 347 348 349 350 351
  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;
352
  if (!self->tensor.defined() || !src_tensor.defined()) {
353 354
    return ToPyObject(res);
  }
355
  res = (self->tensor.impl().get() == src_tensor.impl().get());
356 357 358 359
  return ToPyObject(res);
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

360 361 362
static PyObject* tensor_method_detach(TensorObject* self, PyObject* args,
                                      PyObject* kwargs) {
  EAGER_TRY
363
  PADDLE_ENFORCE_EQ(
364
      self->tensor.initialized(), true,
365
      platform::errors::InvalidArgument("Tensor %s has not been initialized!",
366
                                        self->tensor.name()));
367

368
  PyObject* obj = p_tensor_type->tp_alloc(p_tensor_type, 0);
369
  if (obj) {
370 371 372 373 374 375
    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));
376 377 378 379 380 381 382 383 384 385
    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
}

386 387 388 389
static PyObject* tensor_method_get_underline_tensor(TensorObject* self,
                                                    PyObject* args,
                                                    PyObject* kwargs) {
  EAGER_TRY
390 391 392
  if (self->tensor.is_dense_tensor()) {
    auto* tensor =
        static_cast<paddle::framework::LoDTensor*>(self->tensor.impl().get());
393 394 395 396 397 398 399 400 401
    VLOG(6) << "tensor: " << tensor->IsInitialized();
    return ToPyObject(tensor);
  } else {
    Py_IncRef(Py_None);
    return Py_None;
  }
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

402
// NOTE(wuweilong): Set value and not change self's original place
403 404
static PyObject* tensor_method_set_value(TensorObject* self, PyObject* args,
                                         PyObject* kwargs) {
405
  EAGER_TRY
406
  VLOG(4) << "Value " << self->tensor.name();
407 408
  pybind11::object numpy_value =
      pybind11::object(pybind11::handle(PyTuple_GET_ITEM(args, 0)), true);
409
  InitTensorWithNumpyValue(self, numpy_value, false);
410 411 412 413 414
  Py_INCREF(Py_None);
  return Py_None;
  EAGER_CATCH_AND_THROW_RETURN_NULL
}

415
PyMethodDef variable_methods[] = {
416
    {"numpy", (PyCFunction)(void (*)(void))tensor_method_numpy,
417 418
     METH_VARARGS | METH_KEYWORDS, NULL},
    {"_is_initialized",
419
     (PyCFunction)(void (*)(void))tensor_method__is_initialized,
420
     METH_VARARGS | METH_KEYWORDS, NULL},
421
    {"_copy_to", (PyCFunction)(void (*)(void))tensor_method__copy_to,
422
     METH_VARARGS | METH_KEYWORDS, NULL},
423
    {"copy_", (PyCFunction)(void (*)(void))tensor_method_copy_,
424
     METH_VARARGS | METH_KEYWORDS, NULL},
425
    {"reconstruct_from_",
426
     (PyCFunction)(void (*)(void))tensor_method_reconstruct_from_,
427
     METH_VARARGS | METH_KEYWORDS, NULL},
428
    {"retain_grads", (PyCFunction)(void (*)(void))tensor_retain_grads,
429
     METH_VARARGS | METH_KEYWORDS, NULL},
430
    {"clear_gradient", (PyCFunction)(void (*)(void))tensor_clear_gradient,
431
     METH_VARARGS | METH_KEYWORDS, NULL},
432
    {"_zero_grads", (PyCFunction)(void (*)(void))tensor__zero_grads,
433
     METH_VARARGS | METH_KEYWORDS, NULL},
434
    {"_share_buffer_to", (PyCFunction)(void (*)(void))tensor__share_buffer_to,
435
     METH_VARARGS | METH_KEYWORDS, NULL},
436
    {"_is_shared_buffer_with",
437
     (PyCFunction)(void (*)(void))tensor__is_shared_buffer_with,
438
     METH_VARARGS | METH_KEYWORDS, NULL},
439
    {"_share_underline_tensor_to",
440
     (PyCFunction)(void (*)(void))tensor__share_underline_tensor_to,
441 442
     METH_VARARGS | METH_KEYWORDS, NULL},
    {"_is_shared_underline_tensor_with",
443
     (PyCFunction)(void (*)(void))tensor__is_shared_underline_tensor_with,
444
     METH_VARARGS | METH_KEYWORDS, NULL},
445
    {"detach", (PyCFunction)(void (*)(void))tensor_method_detach,
446
     METH_VARARGS | METH_KEYWORDS, NULL},
447
    {"get_tensor",
448
     (PyCFunction)(void (*)(void))tensor_method_get_underline_tensor,
449
     METH_VARARGS | METH_KEYWORDS, NULL},
450
    {"_set_value", (PyCFunction)(void (*)(void))tensor_method_set_value,
451
     METH_VARARGS | METH_KEYWORDS, NULL},
452 453 454 455
    {NULL, NULL, 0, NULL}};

}  // namespace pybind
}  // namespace paddle