You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

tensor.cpp 36 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  1. /**
  2. * \file imperative/python/src/tensor.cpp
  3. * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
  4. *
  5. * Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
  6. *
  7. * Unless required by applicable law or agreed to in writing,
  8. * software distributed under the License is distributed on an
  9. * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. */
  11. #include "megbrain/dtype.h"
  12. #include "megbrain/common.h"
  13. #include "megbrain/imperative/ops/utility.h"
  14. #include "megbrain/imperative/ops/backward_graph.h"
  15. #include "megbrain/opr/io.h"
  16. #include "./tensor.h"
  17. #include "./grad.h"
  18. #include "./trace.h"
  19. #include "./common.h"
  20. #include "./numpy_dtypes.h"
  21. #include "./graph_rt.h"
  22. #include "./helper.h"
  23. #include <pybind11/numpy.h>
  24. #include <pybind11/operators.h>
  25. #include <range/v3/all.hpp>
  26. #include <string>
  27. #include <unordered_map>
  28. namespace py = pybind11;
  29. namespace views = ranges::views;
  30. namespace mgb::imperative::python {
  31. interpreter::Interpreter::Channel* interpreter_for_py;
  32. PyObject *cpp_apply_with_tracing, *cpp_apply_const_with_tracing;
  33. PyObject *cpp_apply_backward_varnode;
  34. std::shared_ptr<Tensor> make_const(imperative::TensorPtr value) {
  35. if (!(ApplyContext::global_enable & Tensor::Flags::TRACE)) {
  36. return std::make_shared<Tensor>(interpreter_for_py->put(value->dev_tensor()));
  37. }
  38. py::tuple tup(6);
  39. auto data = value->get_value();
  40. tup[0] = py::reinterpret_steal<py::array>(ndarray_from_tensor(data, npy::ShareType::MUST_SHARE));
  41. tup[1] = value->dtype();
  42. tup[2] = value->comp_node();
  43. tup[3] = true;
  44. tup[4] = false;
  45. tup[5] = py::none{};
  46. auto py_ret = PyObject_Call(cpp_apply_const_with_tracing, tup.ptr(), nullptr);
  47. if (!py_ret) throw py::error_already_set();
  48. auto py_list = py::reinterpret_steal<py::list>(py_ret);
  49. auto* tensor_wrapper = TensorWrapper::try_cast(py_list[0].ptr());
  50. auto tensor = tensor_wrapper->m_tensor;
  51. return tensor_wrapper->m_tensor;
  52. }
  53. #define REGISTE_APPLY_FUNC(mode) \
  54. void set_##mode(py::object pyf) { \
  55. mode = pyf.ptr(); \
  56. }
  57. REGISTE_APPLY_FUNC(cpp_apply_with_tracing)
  58. REGISTE_APPLY_FUNC(cpp_apply_const_with_tracing)
  59. REGISTE_APPLY_FUNC(cpp_apply_backward_varnode)
  60. #undef REGISTE_APPLY_FUNC
  61. Tensor::flags_t ApplyContext::global_disable = 0;
  62. Tensor::flags_t ApplyContext::global_enable = 0;
  63. void set_tracing() { ApplyContext::global_enable |= Tensor::Flags::TRACE; }
  64. void unset_tracing() { ApplyContext::global_enable &= ~Tensor::Flags::TRACE; }
  65. bool skip_tracing = false;
  66. apply_result_t apply(ApplyContext& ctx) {
  67. // emulating scalar should be put to specific op's apply, e.g.,
  68. // elementwise, reduce, typecvt. Currently it's still handled at python
  69. // side. It could be move to C++ side if it has an impact on performance
  70. auto flags = ctx.flags & ~ApplyContext::global_disable;
  71. flags = flags | ApplyContext::global_enable;
  72. if (flags & Tensor::Flags::SCALAR) {
  73. // TODO: emulate scalar
  74. }
  75. if (flags & Tensor::Flags::GRAD) {
  76. return apply_grad(ctx);
  77. }
  78. if (auto* op = ctx.op->try_cast_final<GenericPyOp>()) {
  79. py::tuple pyin(ctx.nargs);
  80. for (size_t i = 0; i < ctx.nargs; ++i) {
  81. pyin[i] = TensorWrapper::make(ctx.pytype, ctx.args[i]->shared_from_this());
  82. }
  83. auto f = py::getattr(op->obj, "_default_rule");
  84. auto pyout = py::reinterpret_steal<py::object>(PyObject_Call(f.ptr(), pyin.ptr(), nullptr));
  85. if (!pyout) throw py::error_already_set();
  86. if (auto* tw = TensorWrapper::try_cast(pyout.ptr())) {
  87. return {tw->m_tensor};
  88. }
  89. apply_result_t ret;
  90. ret.reserve(py::len(pyout));
  91. for (auto&& i : pyout) {
  92. auto* tw = TensorWrapper::try_cast(i.ptr());
  93. mgb_assert(tw);
  94. ret.push_back(tw->m_tensor);
  95. }
  96. return ret;
  97. }
  98. if (flags & Tensor::Flags::TRACE) {
  99. return apply_trace(ctx);
  100. } else {
  101. SmallVector<interpreter::Interpreter::Handle> handles(ctx.nargs);
  102. for (size_t i = 0; i < ctx.nargs; ++i) {
  103. handles[i] = ctx.args[i]->m_handle.get();
  104. }
  105. apply_result_t outputs;
  106. // fast copy without really applying
  107. if (ctx.op->same_type<FastpathCopy>()) {
  108. mgb_assert(ctx.nargs == 1);
  109. outputs.reserve(ctx.nargs);
  110. outputs.emplace_back(std::make_shared<Tensor>(ctx.args[0]->m_handle));
  111. return outputs;
  112. }
  113. auto output_handles = interpreter_for_py->apply_op(ctx.op, handles);
  114. outputs.reserve(output_handles.size());
  115. for (auto h : output_handles) {
  116. outputs.emplace_back(std::make_shared<Tensor>(h));
  117. }
  118. return outputs;
  119. }
  120. mgb_assert(0);
  121. }
  122. PyObject* py_apply(PyObject* self, PyObject*const* args, size_t nargs/* , PyObject* kwnames */) {
  123. try {
  124. // if (kwnames && PyTuple_GET_SIZE(kwnames)) {
  125. // PyErr_SetString(PyExc_TypeError, "keyword argument not allowed");
  126. // return nullptr;
  127. // }
  128. if (nargs < 2) {
  129. PyErr_SetString(PyExc_TypeError,
  130. "py_apply expects one Op and at least one tensor "
  131. "as argument");
  132. return nullptr;
  133. }
  134. auto* op = args[0];
  135. PyTypeObject* pytype = args[1]->ob_type;
  136. // check if pytype is Parameter(and all other python Tensor's derived class),
  137. // if yes, using it's tp_base(python Tensor)
  138. if (TensorWrapper::wrap_t::type().same_pytype(pytype->tp_base->tp_base)) {
  139. pytype = pytype->tp_base;
  140. }
  141. ++args;
  142. --nargs;
  143. ApplyContext ctx;
  144. ctx.flags = 0;
  145. ctx.op = py::handle(op).cast<std::shared_ptr<OpDef>>();
  146. SmallVector<Tensor*, 64> tensors(nargs);
  147. ctx.args = &tensors[0];
  148. ctx.nargs = nargs;
  149. ctx.pytype = pytype;
  150. if (py::isinstance<PySymbolVar>(py::handle(args[0]))){
  151. SmallVector<cg::VarNode*> vinputs(nargs);
  152. for (size_t i = 0; i < nargs; ++i) {
  153. vinputs[i] = py::handle(args[i]).cast<PySymbolVar*>()->m_node;
  154. }
  155. auto op = ctx.op.get();
  156. auto rst = OpDef::apply_on_var_node(*op, vinputs);
  157. auto ret = pybind11::tuple(rst.size());
  158. auto typeobj = py::handle(args[0]).get_type();
  159. for (size_t i = 0; i<rst.size(); ++i) {
  160. ret[i] = typeobj(pybind11::cast(rst[i], pybind11::return_value_policy::automatic));
  161. }
  162. return ret.release().ptr();
  163. }
  164. for (size_t i = 0; i < nargs; ++i) {
  165. if (TensorWrapper* tw = TensorWrapper::try_cast(args[i])) {
  166. auto* t = tensors[i] = tw->m_tensor.get();
  167. ctx.flags |= t->m_flags;
  168. } else {
  169. PyErr_SetString(PyExc_TypeError,
  170. ssprintf("op %s expect type Tensor as inputs, got %s actually",
  171. ctx.op->make_name().c_str(), Py_TYPE(args[i])->tp_name).c_str());
  172. return nullptr;
  173. }
  174. }
  175. auto outputs = apply(ctx);
  176. size_t nout = outputs.size();
  177. auto ret = py::tuple(nout);
  178. for (size_t i = 0; i < nout; ++i) {
  179. ret[i] = TensorWrapper::make(pytype, std::move(outputs[i]));
  180. }
  181. return ret.release().ptr();
  182. } catch (std::exception& e) {
  183. PyErr_SetString(PyExc_RuntimeError, e.what());
  184. return nullptr;
  185. }
  186. }
  187. TensorWrapper::TensorWrapper(PyObject* args, PyObject* kwargs) {
  188. if (kwargs && PyDict_Size(kwargs)) {
  189. throw py::type_error("keyword argument not allowed");
  190. }
  191. auto nargs = PyTuple_Size(args);
  192. auto tup = py::reinterpret_borrow<py::tuple>(args);
  193. if (nargs == 0) {
  194. throw py::type_error("too few arguments");
  195. }
  196. if (auto* t = try_cast(tup[0].ptr())) {
  197. if (nargs > 1) {
  198. throw py::type_error("expect 1 argument");
  199. }
  200. m_tensor = t->m_tensor;
  201. } else {
  202. if (nargs == 1) {
  203. auto arg0 = PyTuple_GetItem(args, 0);
  204. // for lazy_eval_tensor
  205. if (strstr(arg0->ob_type->tp_name, "VarNode")) {
  206. if (PyObject_HasAttrString(arg0, "_node")) {
  207. arg0 = PyObject_GetAttrString(arg0, "_node");
  208. }
  209. m_tensor = std::make_shared<Tensor>(py::handle(arg0).cast<cg::VarNode *>());
  210. } else {
  211. // for DeviceTensorND
  212. if (strstr(arg0->ob_type->tp_name, "DeviceTensorND")) {
  213. auto dv = py::handle(arg0).cast<DeviceTensorND>();
  214. interpreter::Interpreter::Handle handle = interpreter_for_py->put(dv);
  215. m_tensor = std::make_shared<Tensor>(handle);
  216. } else {
  217. throw py::type_error("single argument is not tensor, varnode or devicetensor");
  218. }
  219. }
  220. } else {
  221. py::detail::loader_life_support life_sup; // FIXME!!!required to cast DType
  222. if (nargs != 5 && nargs != 6) {
  223. throw py::type_error("expect 5 or 6 arguments");
  224. }
  225. auto data = tup[0].cast<py::array>();
  226. DType dtype = tup[1].cast<DType>();
  227. CompNode cn = tup[2].cast<CompNode>();
  228. bool is_const = tup[3].cast<bool>();
  229. bool no_cache = nargs == 6 ? tup[4].cast<bool>() : false;
  230. std::string name;
  231. if (tup[nargs - 1].ptr() != Py_None) name = tup[nargs - 1].cast<std::string>();
  232. // const op
  233. if (is_const && (ApplyContext::global_enable == Tensor::Flags::TRACE)) {
  234. auto py_ret = PyObject_Call(cpp_apply_const_with_tracing, tup.ptr(), nullptr);
  235. if (!py_ret) throw py::error_already_set();
  236. auto py_list = py::reinterpret_steal<py::list>(py_ret);
  237. if (auto* t = try_cast(py_list[0].ptr())) {
  238. m_tensor = t->m_tensor;
  239. }
  240. return;
  241. }
  242. interpreter::Interpreter::Handle handle;
  243. {
  244. HostTensorND ret(cn);
  245. handle = interpreter_for_py->put(npy::np2tensor(data.ptr(), npy::Meth::copy_into(&ret), dtype), no_cache);
  246. }
  247. m_tensor = std::make_shared<Tensor>(handle);
  248. m_tensor->user_custom_name = name;
  249. if (data.ndim() == 0) {
  250. m_tensor->m_flags |= Tensor::Flags::SCALAR;
  251. }
  252. }
  253. }
  254. }
  255. #define REGISTE_TENSORWRAPPER_FUNC(type, member) \
  256. PyObject* TensorWrapper::member() { \
  257. return py::cast(m_tensor->m_trace_info.member).release().ptr(); \
  258. } \
  259. void TensorWrapper::set_##member(PyObject* dest) { \
  260. auto py_dest = py::reinterpret_borrow<py::object>(dest); \
  261. type real_dest = py_dest.cast<type>(); \
  262. m_tensor->m_trace_info.member = real_dest; \
  263. }
  264. REGISTE_TENSORWRAPPER_FUNC(int64_t, mixin_handle)
  265. REGISTE_TENSORWRAPPER_FUNC(bool, recording)
  266. #undef REGISTE_TENSORWRAPPER_FUNC
  267. #define REGISTE_TENSORWRAPPER_PYOBJECT_FUNC(member) \
  268. PyObject* TensorWrapper::member() { \
  269. if (m_tensor->m_trace_info.member) { \
  270. return m_tensor->m_trace_info.member; \
  271. } else { \
  272. Py_RETURN_NONE; \
  273. } \
  274. } \
  275. void TensorWrapper::set_##member(PyObject* dest) { \
  276. if (dest == Py_None) { \
  277. Py_XDECREF(m_tensor->m_trace_info.member); \
  278. m_tensor->m_trace_info.member = nullptr; \
  279. } else { \
  280. Py_INCREF(dest); \
  281. m_tensor->m_trace_info.member = dest; \
  282. } \
  283. }
  284. REGISTE_TENSORWRAPPER_PYOBJECT_FUNC(compiled_info)
  285. REGISTE_TENSORWRAPPER_PYOBJECT_FUNC(trace_mixin_info)
  286. #undef REGISTE_TENSORWRAPPER_PYOBJECT_FUNC
  287. #define SET_GET_NAME(member) \
  288. PyObject* TensorWrapper::member() { \
  289. return py::cast(m_tensor->member).release().ptr(); \
  290. } \
  291. void TensorWrapper::set_##member(PyObject* dest) { \
  292. auto py_dest = py::reinterpret_borrow<py::object>(dest); \
  293. m_tensor->member = py_dest.cast<std::string>(); \
  294. }
  295. SET_GET_NAME(user_custom_name)
  296. SET_GET_NAME(automatic_name)
  297. #undef SET_GET_NAME
  298. PyObject* TensorWrapper::handle() {
  299. return py::cast(m_tensor->m_handle).release().ptr();
  300. }
  301. void TensorWrapper::set_handle(PyObject* dest) {
  302. auto py_dest = py::reinterpret_borrow<py::object>(dest);
  303. SharedHandle real_dest = py_dest.cast<SharedHandle>();
  304. m_tensor->m_handle = std::move(real_dest);
  305. }
  306. PyObject* TensorWrapper::shape() {
  307. // if it's tracing compiled mode, get value from compiled_info
  308. if (m_tensor->m_trace_info.compiled_info != nullptr) {
  309. if (m_tensor->m_flags & Tensor::Flags::SCALAR) {
  310. return PyTuple_New(0);
  311. }
  312. PyObject *shp = PyObject_GetAttrString(m_tensor->m_trace_info.compiled_info, "shape");
  313. if (shp == Py_None) {
  314. throw TraceReadError("shape of this tensor is not read in trace");
  315. }
  316. return shp;
  317. }
  318. // inside trace, if tensor shape is useful for other operations, set shape_read = true
  319. if (m_tensor->m_trace_info.recording && !skip_tracing) {
  320. PyObject_SetAttrString(m_tensor->m_trace_info.trace_mixin_info, "shape_read", py::cast(true).release().ptr());
  321. }
  322. if (m_tensor->m_flags & Tensor::Flags::SCALAR) {
  323. return PyTuple_New(0);
  324. }
  325. TensorShape shape;
  326. if (m_tensor->m_var) { // get shape from m_var
  327. auto&& mgr = m_tensor->m_var->owner_graph()->static_infer_manager();
  328. auto *tshp = mgr.infer_shape_fallible(m_tensor->m_var);
  329. if (!tshp) {
  330. Py_RETURN_NONE;
  331. }
  332. shape = *tshp;
  333. } else {
  334. shape = m_tensor->shape();
  335. }
  336. if (!shape.ndim) {
  337. Py_RETURN_NONE;
  338. }
  339. py::tuple ret(shape.ndim);
  340. for (size_t i = 0; i < shape.ndim; ++i) {
  341. ret[i] = shape[i];
  342. }
  343. return ret.release().ptr();
  344. }
  345. PyObject* TensorWrapper::dtype() {
  346. if (m_tensor->m_var) {
  347. return py::cast(m_tensor->m_var->dtype()).release().ptr();
  348. }
  349. return py::cast(m_tensor->dtype()).release().ptr();
  350. }
  351. PyObject* TensorWrapper::device() {
  352. if (m_tensor->m_var) {
  353. return py::cast(m_tensor->m_var->comp_node()).release().ptr();
  354. }
  355. return py::cast(m_tensor->comp_node()).release().ptr();
  356. }
  357. PyObject* TensorWrapper::numpy() {
  358. if (m_tensor->m_trace_info.compiled_info != nullptr) {
  359. PyObject* np_val = PyObject_CallMethod(m_tensor->m_trace_info.compiled_info, "numpy", nullptr);
  360. if (!np_val) throw py::error_already_set();
  361. if (np_val == Py_None) {
  362. throw TraceReadError("value of this tensor is not read in trace");
  363. }
  364. if (m_tensor->m_flags & Tensor::Flags::SCALAR) {
  365. PyObject *np_scalar = PyArray_Squeeze(reinterpret_cast<PyArrayObject*>(np_val));
  366. Py_DECREF(np_val);
  367. return np_scalar;
  368. }
  369. return np_val;
  370. }
  371. if (m_tensor->m_trace_info.recording && !skip_tracing) {
  372. PyObject_SetAttrString(m_tensor->m_trace_info.trace_mixin_info, "value_read", py::cast(true).release().ptr());
  373. }
  374. if (m_tensor->m_handle.get() == nullptr && m_tensor->m_var != nullptr) {
  375. auto&& mgr = m_tensor->m_var->owner_graph()->static_infer_manager();
  376. auto&& type = mgr.get_infer_type(m_tensor->m_var);
  377. using InferType = cg::static_infer::InferType;
  378. if (!(type.value & (InferType::CONST | InferType::RT_STATIC))) {
  379. PyErr_SetString(PyExc_ValueError, "tensor invalid");
  380. return nullptr;
  381. }
  382. auto* val = mgr.infer_value_fallible(m_tensor->m_var);
  383. if (!val) {
  384. PyErr_SetString(PyExc_ValueError, "tensor invalid");
  385. return nullptr;
  386. }
  387. auto np_val = py::cast(*val).attr("numpy")();
  388. if (m_tensor->m_flags & Tensor::Flags::SCALAR) {
  389. return PyArray_Squeeze(reinterpret_cast<PyArrayObject*>(np_val.release().ptr()));
  390. }
  391. return np_val.release().ptr();
  392. }
  393. auto&& hv = [&]() {
  394. py::gil_scoped_release _;
  395. return interpreter_for_py->get_value(m_tensor->m_handle.get());
  396. }();
  397. auto arr = py::reinterpret_steal<py::array>(npy::ndarray_from_tensor(hv, npy::ShareType::TRY_SHARE));
  398. if (!arr) {
  399. PyErr_SetString(PyExc_ValueError, "tensor invalid");
  400. return nullptr;
  401. }
  402. if (m_tensor->m_flags & Tensor::Flags::SCALAR) {
  403. mgb_assert(PyArray_Check(arr.ptr()));
  404. return PyArray_Squeeze(reinterpret_cast<PyArrayObject*>(arr.ptr()));
  405. }
  406. return arr.release().ptr();
  407. }
  408. PyObject* TensorWrapper::varnode() {
  409. if (m_tensor->m_var) {
  410. return py::cast(m_tensor->m_var).release().ptr();
  411. }
  412. Py_RETURN_NONE;
  413. }
  414. void TensorWrapper::reset(PyObject* tensor) {
  415. TensorWrapper* t = TensorWrapper::try_cast(tensor);
  416. if (!t) {
  417. throw py::type_error("expect Tensor");
  418. }
  419. std::string user_custom_name = m_tensor->user_custom_name;
  420. std::string automatic_name = m_tensor->automatic_name;
  421. m_tensor = t->m_tensor;
  422. m_tensor->user_custom_name = user_custom_name;
  423. m_tensor->automatic_name = automatic_name;
  424. }
  425. void TensorWrapper::reset_varnode() {
  426. m_tensor->m_var = nullptr;
  427. }
  428. PyObject* TensorWrapper::detach() {
  429. PyObject* self = wrap_t::pycast(this);
  430. PyTypeObject* pytype = self->ob_type;
  431. std::shared_ptr<Tensor> new_tensor;
  432. if (m_tensor->m_handle.get()) {
  433. new_tensor = std::make_shared<Tensor>(m_tensor->m_handle);
  434. } else {
  435. new_tensor = std::make_shared<Tensor>(m_tensor->m_var);
  436. }
  437. new_tensor->m_trace_info = m_tensor->m_trace_info;
  438. new_tensor->m_flags = m_tensor->m_flags;
  439. auto ret = TensorWrapper::make(pytype, std::move(new_tensor));
  440. return ret.release().ptr();
  441. }
  442. PyObject* TensorWrapper::_dev_tensor(){
  443. if (m_tensor->m_trace_info.compiled_info != nullptr) {
  444. auto *dev_tensor = PyObject_CallMethod(m_tensor->m_trace_info.compiled_info, "_dev_tensor", nullptr);
  445. if (!dev_tensor) throw py::error_already_set();
  446. if (dev_tensor == Py_None) {
  447. throw TraceReadError("raw data of this tensor is not read in trace");
  448. }
  449. // set m_handle to make it a real tensor
  450. auto py_dev_tensor = py::reinterpret_borrow<py::object>(dev_tensor);
  451. auto sh = interpreter_for_py->put(py_dev_tensor.cast<DeviceTensorND>());
  452. m_tensor->m_handle = std::move(SharedHandle(sh));
  453. // compiled info is useless after m_handle is set
  454. Py_DECREF(m_tensor->m_trace_info.compiled_info);
  455. m_tensor->m_trace_info.compiled_info = nullptr;
  456. return dev_tensor;
  457. }
  458. if (m_tensor->m_trace_info.recording && !skip_tracing) {
  459. PyObject_SetAttrString(m_tensor->m_trace_info.trace_mixin_info, "data_read", py::cast(true).release().ptr());
  460. }
  461. auto dev_tensor = [&](){
  462. py::gil_scoped_release _;
  463. return interpreter_for_py->get_dev_tensor(m_tensor->m_handle.get());
  464. }();
  465. return py::cast(dev_tensor).release().ptr();
  466. }
  467. void TensorWrapper::_swap_out() {
  468. interpreter_for_py->swap_out(m_tensor->m_handle.get());
  469. }
  470. void TensorWrapper::_swap_in() {
  471. interpreter_for_py->swap_in(m_tensor->m_handle.get());
  472. }
  473. void TensorWrapper::_drop() {
  474. interpreter_for_py->drop(m_tensor->m_handle.get());
  475. }
  476. PyObject* TensorWrapper::isscalar() {
  477. if(m_tensor->m_flags & Tensor::Flags::SCALAR) {
  478. Py_RETURN_TRUE;
  479. } else {
  480. Py_RETURN_FALSE;
  481. }
  482. }
  483. void TensorWrapper::setscalar() {
  484. m_tensor->m_flags |= Tensor::Flags::SCALAR;
  485. }
  486. void TensorWrapper::unsetscalar() {
  487. m_tensor->m_flags &= ~Tensor::Flags::SCALAR;
  488. }
  489. struct TensorWeakRef {
  490. std::weak_ptr<Tensor> wptr;
  491. TensorWeakRef(const TensorWrapper& tw) : wptr(tw.m_tensor) {}
  492. py::object operator()() {
  493. if (auto p = wptr.lock()) {
  494. return TensorWrapper::make(p);
  495. }
  496. return py::none();
  497. }
  498. int _use_cnt() { return wptr.use_count(); }
  499. };
  500. /* ============== convert inputs ============== */
  501. // map numpy.dtype.kind to priority
  502. inline uint8_t category_priority(char c) {
  503. switch (c) {
  504. case 'f': return 3; // floating-point
  505. case 'i': return 2; // signed integer
  506. case 'u': return 2; // unsigned integer
  507. case 'b': return 1; // boolean
  508. default: return 0;
  509. }
  510. }
  511. // Returns the maximum value of the priority of each type in the list `types`.
  512. uint8_t max_priority(SmallVector<PyArray_Descr*> types) {
  513. if (types.size() == 0) {
  514. return 0;
  515. } else {
  516. uint8_t max_p = 0;
  517. for (auto&& desc: types) {
  518. max_p = std::max(max_p, category_priority(desc->kind));
  519. }
  520. return max_p;
  521. }
  522. }
  523. // Returns the data type with sufficient size to hold all types of
  524. // category `cat` in the list `types`.
  525. PyArray_Descr* promote_types(SmallVector<PyArray_Descr*> types, uint8_t cat) {
  526. // Return value: New reference
  527. SmallVector<PyArray_Descr*> used_types;
  528. for (auto&& desc: types) {
  529. auto&& v = category_priority(desc->kind);
  530. if (v == cat) {
  531. used_types.emplace_back(desc);
  532. }
  533. }
  534. mgb_assert(used_types.size() > 0, "size of used_types is 0");
  535. PyArray_Descr* res = used_types[0];
  536. Py_INCREF(res);
  537. for (size_t i = 1; i < used_types.size(); ++i) {
  538. PyArray_Descr* tmp = PyArray_PromoteTypes(used_types[i], res);
  539. Py_DECREF(res);
  540. res = tmp;
  541. }
  542. return res;
  543. }
  544. PyArray_Descr* scalar2dtype(PyObject* arg) {
  545. // Return value: New reference
  546. if (PyBool_Check(arg)) {
  547. auto&& descr = PyArray_DescrFromType(NPY_BOOL);
  548. return descr;
  549. }
  550. if (PyLong_CheckExact(arg)) {
  551. auto&& descr = PyArray_DescrFromType(NPY_INT32);
  552. return descr;
  553. }
  554. if (PyFloat_CheckExact(arg)) {
  555. auto&& descr = PyArray_DescrFromType(NPY_FLOAT32);
  556. return descr;
  557. }
  558. return nullptr;
  559. }
  560. PyArray_Descr* _dtype_promotion(PyObject*const* args, size_t nargs) {
  561. // Return value: New reference
  562. SmallVector<PyArray_Descr*> tensors;
  563. SmallVector<PyArray_Descr*> scalars;
  564. bool is_tuple = false;
  565. PyObject* tuple = nullptr;
  566. if (nargs == 1 && (PyTuple_Check(args[0]) || PyList_Check(args[0]))) {
  567. if (PyList_Check(args[0])) {
  568. tuple = PyList_AsTuple(args[0]);
  569. } else {
  570. tuple = args[0];
  571. Py_INCREF(tuple);
  572. }
  573. nargs = PyTuple_Size(tuple);
  574. is_tuple = true;
  575. }
  576. for (size_t i = 0; i < nargs; ++i) {
  577. PyObject* handle = is_tuple ? PyTuple_GetItem(tuple, i): args[i];
  578. if (handle == Py_None) continue;
  579. TensorWrapper* tw = TensorWrapper::try_cast(handle);
  580. if (tw) {
  581. mgb::DType type = tw->m_tensor->dtype();
  582. auto&& descr = npy::dtype_mgb2np_descr(type);
  583. Py_INCREF(descr.get());
  584. tensors.emplace_back(descr.get());
  585. }else{
  586. if (PyArray_Check(handle) || PyArray_CheckScalar(handle)) {
  587. auto&& descr = PyArray_DescrFromObject(handle, nullptr);
  588. tensors.emplace_back(descr);
  589. continue;
  590. }
  591. if (py::isinstance<PySymbolVar>(py::handle(handle))){
  592. auto var = py::handle(handle).cast<PySymbolVar*>();
  593. mgb::DType type = var->m_node->dtype();
  594. auto && descr = npy::dtype_mgb2np_descr(type);
  595. Py_INCREF(descr.get());
  596. tensors.emplace_back(descr.get());
  597. continue;
  598. }
  599. PyArray_Descr* descr = scalar2dtype(handle);
  600. if (descr) {
  601. scalars.emplace_back(descr);
  602. continue;
  603. }
  604. }
  605. }
  606. auto max_pri_scalars = max_priority(scalars);
  607. auto max_pri_tensors = max_priority(tensors);
  608. if (max_pri_scalars <= 0 && max_pri_tensors <= 0) {
  609. throw py::value_error("invalid input, no dtype avaliable");
  610. }
  611. PyArray_Descr* res;
  612. if (max_pri_scalars > max_pri_tensors) {
  613. res = promote_types(scalars, max_pri_scalars);
  614. }else{
  615. res = promote_types(tensors, max_pri_tensors);
  616. }
  617. for (auto *p: tensors) { Py_DECREF(p); }
  618. for (auto *p: scalars) { Py_DECREF(p); }
  619. Py_XDECREF(tuple);
  620. return res;
  621. }
  622. CompNode _get_device(PyObject*const* args, size_t nargs) {
  623. bool is_tuple = false;
  624. PyObject* tuple = nullptr;
  625. if (nargs == 1 && (PyTuple_Check(args[0]) || PyList_Check(args[0]))) {
  626. if (PyList_Check(args[0])) {
  627. tuple = PyList_AsTuple(args[0]);
  628. } else {
  629. tuple = args[0];
  630. Py_INCREF(tuple);
  631. }
  632. nargs = PyTuple_Size(tuple);
  633. is_tuple = true;
  634. }
  635. bool valid = false;
  636. CompNode cn;
  637. for (size_t i = 0; i < nargs; ++i) {
  638. PyObject* handle = is_tuple ? PyTuple_GetItem(tuple, i) : args[i];
  639. TensorWrapper* tw = TensorWrapper::try_cast(handle);
  640. bool is_symvar = py::isinstance<PySymbolVar>(py::handle(handle));
  641. if (tw || is_symvar) {
  642. if (!valid) {
  643. cn = tw ? tw->m_tensor->comp_node()
  644. : py::handle(handle)
  645. .cast<PySymbolVar*>()
  646. ->m_node->comp_node();
  647. valid = true;
  648. } else {
  649. CompNode cn1 = tw ? tw->m_tensor->comp_node()
  650. : py::handle(handle)
  651. .cast<PySymbolVar*>()
  652. ->m_node->comp_node();
  653. if (cn1 != cn) {
  654. throw py::value_error(ssprintf("ambiguous device: %s vs %s",
  655. cn.to_string().c_str(),
  656. cn1.to_string().c_str()));
  657. }
  658. }
  659. }
  660. }
  661. if (!valid) {
  662. return CompNode::load(get_default_device());
  663. }
  664. Py_XDECREF(tuple);
  665. return cn;
  666. }
  667. // Returns the dtype that would result from performing an arithmetic
  668. // operation on the provided input tensors and scalars.
  669. PyObject* dtype_promotion(PyObject* self, PyObject*const* args, size_t nargs) {
  670. if (!nargs) {
  671. PyErr_SetString(PyExc_TypeError, "empty input is not allowed");
  672. return nullptr;
  673. }
  674. try {
  675. PyArray_Descr* res = _dtype_promotion(args, nargs);
  676. return py::cast(npy::dtype_np2mgb_descr(res)).release().ptr();
  677. } catch (std::exception& e) {
  678. PyErr_SetString(PyExc_RuntimeError, e.what());
  679. return nullptr;
  680. }
  681. }
  682. PyObject* get_device(PyObject* self, PyObject*const* args, size_t nargs) {
  683. if (!nargs) {
  684. PyErr_SetString(PyExc_TypeError, "empty input is not allowed");
  685. return nullptr;
  686. }
  687. try {
  688. CompNode cn = _get_device(args, nargs);
  689. return py::cast(cn).release().ptr();
  690. } catch (std::exception& e) {
  691. PyErr_SetString(PyExc_RuntimeError, e.what());
  692. return nullptr;
  693. }
  694. }
  695. #ifdef METH_FASTCALL
  696. #define MGE_PY_INTERFACE(NAME, FUNC) \
  697. { #NAME, (PyCFunction)FUNC, METH_FASTCALL, nullptr }
  698. #else
  699. #define WRAP_FUNC_PY35(FUNC) \
  700. PyObject* py35_##FUNC(PyObject* self, PyObject* args) { \
  701. auto* arr = &PyTuple_GET_ITEM(args, 0); \
  702. auto size = PyTuple_GET_SIZE(args); \
  703. return FUNC(self, arr, size); \
  704. }
  705. WRAP_FUNC_PY35(py_apply);
  706. WRAP_FUNC_PY35(dtype_promotion);
  707. WRAP_FUNC_PY35(get_device);
  708. #undef WRAP_FUNC_PY35
  709. #define MGE_PY_INTERFACE(NAME, FUNC) \
  710. { #NAME, (PyCFunction)py35_##FUNC, METH_VARARGS, nullptr }
  711. #endif
  712. void init_tensor(py::module m) {
  713. imperative::Tensor::static_initialize();
  714. static auto sl_interpreter_for_py = interpreter::Interpreter::inst().create_channel();
  715. interpreter_for_py = sl_interpreter_for_py.get();
  716. auto* tensor_type = TensorWrapper::wrap_t::type()
  717. .def<&TensorWrapper::numpy>("numpy")
  718. .def_getset<&TensorWrapper::shape>("shape")
  719. .def_getset<&TensorWrapper::dtype>("dtype")
  720. .def_getset<&TensorWrapper::device>("device")
  721. .def<&TensorWrapper::reset>("_reset")
  722. .def<&TensorWrapper::isscalar>("_isscalar")
  723. .def<&TensorWrapper::setscalar>("_setscalar")
  724. .def<&TensorWrapper::unsetscalar>("_unsetscalar")
  725. .def<&TensorWrapper::detach>("detach")
  726. .def<&TensorWrapper::_dev_tensor>("_dev_tensor")
  727. .def<&TensorWrapper::_swap_out>("_swap_out")
  728. .def<&TensorWrapper::_swap_in>("_swap_in")
  729. .def<&TensorWrapper::_drop>("_drop")
  730. .def<&TensorWrapper::reset_varnode>("_reset_varnode")
  731. .def<&TensorWrapper::_use_cnt>("_use_cnt")
  732. .def_getset<&TensorWrapper::varnode>("_varnode")
  733. .def_getset<&TensorWrapper::mixin_handle, &TensorWrapper::set_mixin_handle>("_mixin_handle")
  734. .def_getset<&TensorWrapper::recording, &TensorWrapper::set_recording>("_recording")
  735. .def_getset<&TensorWrapper::handle, &TensorWrapper::set_handle>("_handle")
  736. .def_getset<&TensorWrapper::compiled_info, &TensorWrapper::set_compiled_info>("_compiled_info")
  737. .def_getset<&TensorWrapper::trace_mixin_info, &TensorWrapper::set_trace_mixin_info>("_trace_mixin_info")
  738. .def_getset<&TensorWrapper::user_custom_name, &TensorWrapper::set_user_custom_name>("c_name")
  739. .def_getset<&TensorWrapper::automatic_name, &TensorWrapper::set_automatic_name>("_name")
  740. .finalize();
  741. if (!tensor_type) throw py::error_already_set();
  742. py::setattr(m, "Tensor", tensor_type);
  743. py::class_<TensorWeakRef>(m, "TensorWeakRef")
  744. .def(py::init<const TensorWrapper&>())
  745. .def("__call__", &TensorWeakRef::operator())
  746. .def("_use_cnt", &TensorWeakRef::_use_cnt);
  747. py::class_<PySymbolVar, std::shared_ptr<PySymbolVar>>(m, "SymbolVar")
  748. .def_property_readonly(
  749. "dtype", [](PySymbolVar* v) { return v->m_node->dtype(); })
  750. .def_property("var", [](PySymbolVar* v) { return v->m_node; },
  751. [](PySymbolVar* s, cg::VarNode* v) { s->m_node = v; })
  752. .def_property_readonly(
  753. "device",
  754. [](PySymbolVar* v) { return v->m_node->comp_node(); })
  755. .def_property_readonly(
  756. "graph",
  757. [](PySymbolVar* v) { return v->m_node->owner_graph(); })
  758. .def_property_readonly(
  759. "shape",
  760. [](PySymbolVar* v) -> const TensorShape* {
  761. auto&& mgr = v->m_node->owner_graph()
  762. ->static_infer_manager();
  763. return mgr.infer_shape_fallible(v->m_node);
  764. })
  765. .def("_isscalar", [](PySymbolVar* v) { return v->is_scalar; })
  766. .def("_setscalar",
  767. [](PySymbolVar* v) { return v->is_scalar = true; })
  768. .def(py::init([](cg::VarNode* node) {
  769. return std::make_shared<PySymbolVar>(node);
  770. }),
  771. py::arg() = nullptr);
  772. static PyMethodDef method_defs[] = {
  773. MGE_PY_INTERFACE(apply, py_apply),
  774. MGE_PY_INTERFACE(dtype_promotion, dtype_promotion),
  775. MGE_PY_INTERFACE(get_device, get_device),
  776. {nullptr, nullptr, 0, nullptr}};
  777. for (auto&& def: method_defs) {
  778. if (def.ml_meth != nullptr) {
  779. auto* func = PyCFunction_NewEx(&def, nullptr, nullptr);
  780. if (!func) throw py::error_already_set();
  781. py::setattr(m, def.ml_name, func);
  782. }
  783. }
  784. static constexpr auto sync_py_task_q = []{
  785. py::gil_scoped_release _;
  786. py_task_q.wait_all_task_finish();
  787. };
  788. m.def("set_option",
  789. [](std::string name, size_t value){ interpreter_for_py->set_option(name, value); });
  790. m.def("get_option",
  791. [](std::string name){ return interpreter_for_py->get_option(name); });
  792. m.def("_set_swap_flag",
  793. [](bool flag) { interpreter_for_py->set_option("enable_swap", flag); });
  794. m.def("_set_drop_flag",
  795. [](bool flag) { interpreter_for_py->set_option("enable_drop", flag); });
  796. m.def("config_async_level",
  797. [](int level) {
  798. mgb_assert(level >= 0 and level <= 2, "async_level should be 0, 1 or 2");
  799. interpreter_for_py->set_option("async_level", level);
  800. });
  801. m.def("get_async_level",
  802. []() { return interpreter_for_py->get_option("async_level"); });
  803. m.def("set_buffer_length",
  804. [](int length) {
  805. mgb_assert(length >= 0 and length < 100, "buffer_length should be in [0, 100)");
  806. interpreter_for_py->set_option("buffer_length", length);
  807. });
  808. m.def("push_scope",
  809. [](std::string name) { interpreter_for_py->push_scope(name); });
  810. m.def("pop_scope",
  811. [](std::string name) { interpreter_for_py->pop_scope(name); });
  812. m.def("start_profile",
  813. [](std::unordered_map<std::string, int> option) { return interpreter_for_py->start_profile(option); });
  814. m.def("stop_profile",
  815. [](std::string basename, std::string format) { interpreter_for_py->stop_profile(basename, format); });
  816. m.def("sync",
  817. []() {
  818. interpreter_for_py->sync();
  819. sync_py_task_q();
  820. });
  821. m.def("full_sync",
  822. []() {
  823. interpreter_for_py->sync();
  824. CompNode::sync_all();
  825. sync_py_task_q();
  826. });
  827. m.def("close",
  828. []() {
  829. interpreter_for_py->close();
  830. sync_py_task_q();
  831. });
  832. py::handle grad_key_type = GradKeyWrapper::wrap_t::type()
  833. .def<&GradKeyWrapper::attach>("attach")
  834. .def<&GradKeyWrapper::is_attached_to>("is_attached_to")
  835. .def_getset<&GradKeyWrapper::get_name, &GradKeyWrapper::set_name>("name")
  836. .finalize();
  837. if (!grad_key_type) throw py::error_already_set();
  838. py::setattr(m, "GradKey", grad_key_type);
  839. m.def("backward", &GradKeyWrapper::backward);
  840. m.def("set_cpp_apply_with_tracing", &set_cpp_apply_with_tracing);
  841. m.def("set_cpp_apply_const_with_tracing", &set_cpp_apply_const_with_tracing);
  842. m.def("set_cpp_apply_backward_varnode", &set_cpp_apply_backward_varnode);
  843. m.attr("skip_tracing") = &skip_tracing;
  844. py::class_<SharedHandle>(m, "SharedHandle")
  845. .def(py::init<const SharedHandle&>())
  846. .def("__eq__", [](SharedHandle &thish, SharedHandle &thath) {
  847. return (thish.get() == thath.get());
  848. })
  849. .def("__hash__", [](SharedHandle &sh) {
  850. return reinterpret_cast<int64_t>(sh.get());
  851. })
  852. ;
  853. m.def("set_tracing", &set_tracing);
  854. m.def("unset_tracing", &unset_tracing);
  855. }
  856. #undef MGE_PY_INTERFACE
  857. } // namespace mgb::imperative::python

MegEngine 安装包中集成了使用 GPU 运行代码所需的 CUDA 环境,不用区分 CPU 和 GPU 版。 如果想要运行 GPU 程序,请确保机器本身配有 GPU 硬件设备并安装好驱动。 如果你想体验在云端 GPU 算力平台进行深度学习开发的感觉,欢迎访问 MegStudio 平台