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.

ops.cpp 17 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /**
  2. * \file imperative/python/src/ops.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 "./ops.h"
  12. #include "megbrain/imperative.h"
  13. #include "megbrain/imperative/ops/backward_graph.h"
  14. #include "megbrain/imperative/ops/opr_attr.h"
  15. #include "megbrain/imperative/ops/utility.h"
  16. #include "megbrain/imperative/ops/autogen.h"
  17. #include <Python.h>
  18. #include <unordered_map>
  19. namespace py = pybind11;
  20. using namespace mgb::imperative;
  21. namespace {
  22. auto normalize_enum(const std::string& in) {
  23. std::string ret;
  24. for (auto&& c : in) {
  25. ret += toupper(c);
  26. }
  27. return ret;
  28. }
  29. } // anonymous namespace
  30. #define CATCH_ALL(RETVAL) \
  31. catch(py::error_already_set& e) { \
  32. e.restore(); \
  33. return RETVAL; \
  34. } catch(py::builtin_exception& e) { \
  35. e.set_error(); \
  36. return RETVAL; \
  37. } catch(std::exception& e) { \
  38. PyErr_SetString(PyExc_RuntimeError, e.what()); \
  39. return RETVAL; \
  40. } \
  41. namespace {
  42. #define PyOp(name) Py##name
  43. #define PyOpType(name) PyOp(name)::py_type
  44. #define PyOpDefBegin(name) \
  45. struct PyOp(name) : PyOpDef { \
  46. using Ty = name; \
  47. Ty& inst() { return op->cast_final_safe<Ty>(); } \
  48. static PyTypeObject py_type;
  49. #define PyOpDefEnd(name) \
  50. }; \
  51. PyTypeObject PyOpType(name);
  52. #define RETURN_RICHCOMPARE(val1, val2, op) \
  53. do { \
  54. switch (op) { \
  55. case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
  56. case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
  57. case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
  58. case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
  59. case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
  60. case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
  61. default: \
  62. Py_FatalError("Unreachable C code path reached"); \
  63. } \
  64. } while (0)
  65. template <typename T, typename SFINAE = void>
  66. struct pyobj_convert_generic {
  67. static T from(PyObject* obj) {
  68. // TODO: remove this guard which is used for pybind11 implicit conversion
  69. py::detail::loader_life_support guard{};
  70. return py::cast<T>(py::handle(obj));
  71. }
  72. template<typename U,
  73. typename = std::enable_if_t<std::is_same_v<T, std::decay_t<U>>>>
  74. static PyObject* to(U&& t) {
  75. return py::cast(std::forward<U>(t)).release().ptr();
  76. }
  77. };
  78. template <typename T>
  79. struct EnumTrait {
  80. static constexpr bool is_bit_combined = false;
  81. };
  82. template <typename T>
  83. PyObject* py_new_generic(PyTypeObject* type, PyObject*, PyObject*) {
  84. PyObject* obj = type->tp_alloc(type, 0);
  85. T* self = reinterpret_cast<T*>(obj);
  86. if (self != NULL) {
  87. self->op = T::Ty::make();
  88. }
  89. return obj;
  90. }
  91. template<typename T>
  92. void py_dealloc_generic(PyObject* obj) {
  93. reinterpret_cast<T*>(obj)->op.reset();
  94. Py_TYPE(obj)->tp_free(obj);
  95. }
  96. template<typename T, typename U, U T::Ty::*attr>
  97. PyObject* py_get_generic_impl(PyObject* obj, void* /* closure */) {
  98. auto& op = reinterpret_cast<T*>(obj)->inst();
  99. return pyobj_convert_generic<U>::to(op.*attr);
  100. }
  101. #define py_get_generic(name, attr) \
  102. py_get_generic_impl<PyOp(name), decltype(std::declval<name>().attr), &name::attr>
  103. template<typename T, typename U, U T::Ty::*attr>
  104. int py_set_generic_impl(PyObject* obj, PyObject* value, void* /* closure */) {
  105. if (value == NULL) {
  106. PyErr_SetString(PyExc_TypeError, "Cannot delete the attribute");
  107. return -1;
  108. }
  109. auto& op = reinterpret_cast<T*>(obj)->inst();
  110. try {
  111. op.*attr = pyobj_convert_generic<U>::from(value);
  112. } CATCH_ALL(-1)
  113. return 0;
  114. }
  115. #define py_set_generic(name, attr) \
  116. py_set_generic_impl<PyOp(name), decltype(std::declval<name>().attr), &name::attr>
  117. struct PyOpDef {
  118. PyObject_HEAD
  119. std::shared_ptr<OpDef> op;
  120. static PyTypeObject py_type;
  121. static std::unordered_map<mgb::Typeinfo*, PyTypeObject*> ctype2pytype;
  122. static PyGetSetDef py_getsetters[];
  123. static Py_hash_t tp_hash(PyObject *obj);
  124. static PyObject* tp_richcompare(PyObject *self, PyObject *other, int op);
  125. };
  126. PyTypeObject PyOpType(OpDef);
  127. std::unordered_map<mgb::Typeinfo*, PyTypeObject*> PyOp(OpDef)::ctype2pytype;
  128. PyObject* py_get_scope(PyObject* obj, void* /* closure */) {
  129. return pyobj_convert_generic<std::string>::to(
  130. reinterpret_cast<PyOp(OpDef)*>(obj)->op->scope());
  131. }
  132. int py_set_scope(PyObject* obj, PyObject* value, void* /* closure */) {
  133. if (value == NULL) {
  134. PyErr_SetString(PyExc_TypeError, "Cannot delete the attribute");
  135. return -1;
  136. }
  137. try {
  138. reinterpret_cast<PyOp(OpDef)*>(obj)->op
  139. ->set_scope(pyobj_convert_generic<std::string>::from(value));
  140. } CATCH_ALL(-1)
  141. return 0;
  142. }
  143. PyGetSetDef PyOp(OpDef)::py_getsetters[] = {
  144. {const_cast<char*>("scope"), py_get_scope, py_set_scope, "scope", NULL},
  145. {NULL}
  146. };
  147. Py_hash_t PyOp(OpDef)::tp_hash(PyObject *obj) {
  148. return static_cast<Py_hash_t>(
  149. reinterpret_cast<PyOp(OpDef)*>(obj)->op->hash());
  150. }
  151. PyObject* PyOp(OpDef)::tp_richcompare(PyObject *self, PyObject *other, int op) {
  152. bool same = reinterpret_cast<PyOp(OpDef)*>(self)->op->is_same(
  153. *reinterpret_cast<PyOp(OpDef)*>(other)->op);
  154. if (op == Py_EQ || op == Py_NE) {
  155. RETURN_RICHCOMPARE(same, true, op);
  156. }
  157. Py_RETURN_NOTIMPLEMENTED;
  158. }
  159. template<typename T>
  160. struct EnumWrapper {
  161. static_assert(std::is_enum_v<T>);
  162. PyObject_HEAD
  163. T value;
  164. static const char* name;
  165. static PyTypeObject type;
  166. static std::unordered_map<T, std::string> type2str;
  167. static std::unordered_map<std::string, T> str2type;
  168. EnumWrapper() = default;
  169. EnumWrapper(T v): value(v) {}
  170. EnumWrapper(std::string&& str): EnumWrapper(str2type.at(normalize_enum(str))) {}
  171. std::string to_string() const {
  172. return type2str.at(value);
  173. }
  174. static PyObject* py_repr(PyObject* self) {
  175. return pyobj_convert_generic<std::string>::to(
  176. std::string(name) + "." + reinterpret_cast<EnumWrapper*>(self)->to_string());
  177. }
  178. static PyObject* tp_richcompare(PyObject *self, PyObject *other, int op) {
  179. T lhs = reinterpret_cast<EnumWrapper*>(self)->value,
  180. rhs = reinterpret_cast<EnumWrapper*>(other)->value;
  181. if (op == Py_EQ || op == Py_NE) {
  182. RETURN_RICHCOMPARE(lhs, rhs, op);
  183. }
  184. Py_RETURN_NOTIMPLEMENTED;
  185. }
  186. };
  187. template <typename T>
  188. struct pyobj_convert_generic<T,
  189. std::enable_if_t<std::is_enum_v<std::decay_t<T>> &&
  190. !EnumTrait<T>::is_bit_combined>> {
  191. using Wrapper = EnumWrapper<T>;
  192. static T from(PyObject* obj) {
  193. if (PyObject_TypeCheck(obj, &Wrapper::type)) {
  194. return reinterpret_cast<Wrapper*>(obj)->value;
  195. }
  196. // try as string
  197. // TODO: type checkcd
  198. return Wrapper(pyobj_convert_generic<std::string>::from(obj)).value;
  199. }
  200. static PyObject* to(T t) {
  201. PyTypeObject* pytype = &Wrapper::type;
  202. PyObject* obj = pytype->tp_alloc(pytype, 0);
  203. reinterpret_cast<Wrapper*>(obj)->value = t;
  204. return obj;
  205. }
  206. };
  207. template<typename T>
  208. struct BitCombinedEnumWrapper {
  209. static_assert(std::is_enum_v<T>);
  210. PyObject_HEAD
  211. T value;
  212. static const char* name;
  213. static PyTypeObject type;
  214. static std::unordered_map<T, std::string> type2str;
  215. static std::unordered_map<std::string, T> str2type;
  216. static PyNumberMethods number_methods;
  217. BitCombinedEnumWrapper() = default;
  218. BitCombinedEnumWrapper(T v): value(v) {}
  219. BitCombinedEnumWrapper(std::string&& str)
  220. : BitCombinedEnumWrapper(str2type.at(normalize_enum(str))) {}
  221. std::string to_string() const {
  222. if (static_cast<uint32_t>(value) == 0) {
  223. return "None";
  224. } else {
  225. auto ret = std::string();
  226. bool first = true;
  227. for (uint32_t i = 0; i < 32; i++) {
  228. uint32_t value_int = static_cast<uint32_t>(value);
  229. auto it = type2str.find(static_cast<T>((1 << i) & value_int));
  230. if (it != type2str.end()) {
  231. if (!first) {
  232. ret += " + ";
  233. } else {
  234. first = false;
  235. }
  236. ret += (std::string(name) + "." + it->second);
  237. }
  238. }
  239. return ret;
  240. }
  241. }
  242. static PyObject* py_new_combined_enum(PyTypeObject* type, PyObject*, PyObject*) {
  243. PyObject* obj = type->tp_alloc(type, 0);
  244. reinterpret_cast<BitCombinedEnumWrapper*>(obj)->value = static_cast<T>(1);
  245. return obj;
  246. }
  247. static int py_init(PyObject* self, PyObject* args, PyObject*) {
  248. int input = 1;
  249. if (PyArg_ParseTuple(args, "|i", &input)){
  250. reinterpret_cast<BitCombinedEnumWrapper*>(self)->value =
  251. static_cast<T>(input);
  252. }
  253. return 0;
  254. }
  255. static PyObject* py_repr(PyObject* self) {
  256. return pyobj_convert_generic<std::string>::to(
  257. reinterpret_cast<BitCombinedEnumWrapper*>(self)->to_string());
  258. }
  259. static PyObject* py_or(PyObject* self, PyObject* other) {
  260. if(!(self->ob_type == other->ob_type)){
  261. return PyErr_Format(
  262. PyExc_RuntimeError,
  263. "Operand in or operator must be the same type.");
  264. }
  265. PyObject* obj = type.tp_alloc(&type, 0);
  266. T lhs = reinterpret_cast<BitCombinedEnumWrapper*>(self)->value,
  267. rhs = reinterpret_cast<BitCombinedEnumWrapper*>(other)->value;
  268. reinterpret_cast<BitCombinedEnumWrapper*>(obj)->value = static_cast<T>(
  269. static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
  270. return obj;
  271. }
  272. static PyObject* py_and(PyObject* self, PyObject* other) {
  273. if (!(self->ob_type == other->ob_type)) {
  274. return PyErr_Format(
  275. PyExc_RuntimeError,
  276. "Operand in and operator must be the same type.");
  277. }
  278. PyObject* obj = type.tp_alloc(&type, 0);
  279. T lhs = reinterpret_cast<BitCombinedEnumWrapper*>(self)->value,
  280. rhs = reinterpret_cast<BitCombinedEnumWrapper*>(other)->value;
  281. reinterpret_cast<BitCombinedEnumWrapper*>(obj)->value = static_cast<T>(
  282. static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs));
  283. return obj;
  284. }
  285. static PyObject* tp_richcompare(PyObject* self, PyObject* other, int op) {
  286. T lhs = reinterpret_cast<BitCombinedEnumWrapper*>(self)->value,
  287. rhs = reinterpret_cast<BitCombinedEnumWrapper*>(other)->value;
  288. if (op == Py_EQ || op == Py_NE) {
  289. RETURN_RICHCOMPARE(lhs, rhs, op);
  290. }
  291. Py_RETURN_NOTIMPLEMENTED;
  292. }
  293. };
  294. template <typename T>
  295. struct pyobj_convert_generic<T,
  296. std::enable_if_t<std::is_enum_v<std::decay_t<T>> &&
  297. EnumTrait<T>::is_bit_combined>> {
  298. using Wrapper = BitCombinedEnumWrapper<T>;
  299. static T from(PyObject* obj) {
  300. if (PyObject_TypeCheck(obj, &Wrapper::type)) {
  301. return reinterpret_cast<Wrapper*>(obj)->value;
  302. }
  303. // try as string
  304. // TODO: type checkcd
  305. return Wrapper(pyobj_convert_generic<std::string>::from(obj)).value;
  306. }
  307. static PyObject* to(T t) {
  308. PyTypeObject* pytype = &Wrapper::type;
  309. PyObject* obj = pytype->tp_alloc(pytype, 0);
  310. reinterpret_cast<Wrapper*>(obj)->value = t;
  311. return obj;
  312. }
  313. };
  314. void _init_py_op_def(py::module m) {
  315. using py_op = PyOp(OpDef);
  316. auto& py_type = PyOpType(OpDef);
  317. py_type = {PyVarObject_HEAD_INIT(NULL, 0)};
  318. py_type.tp_name = "megengine.core._imperative_rt.OpDef";
  319. py_type.tp_basicsize = sizeof(PyOp(OpDef));
  320. py_type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
  321. py_type.tp_doc = "OpDef";
  322. py_type.tp_base = &PyBaseObject_Type;
  323. py_type.tp_hash = PyOp(OpDef)::tp_hash;
  324. py_type.tp_richcompare = PyOp(OpDef)::tp_richcompare;
  325. py_type.tp_getset = py_op::py_getsetters;
  326. mgb_assert(PyType_Ready(&py_type) >= 0);
  327. m.add_object("OpDef", reinterpret_cast<PyObject*>(&py_type));
  328. }
  329. /*********** begin of hand-write opdefs **************/
  330. PyOpDefBegin(BackwardGraph) // {{
  331. // };
  332. PyOpDefEnd(BackwardGraph)
  333. void _init_py_backward_graph(py::module m) {
  334. using py_op = PyOp(BackwardGraph);
  335. auto& py_type = PyOpType(BackwardGraph);
  336. py_type = {PyVarObject_HEAD_INIT(NULL, 0)};
  337. py_type.tp_name = "megengine.core._imperative_rt.ops.BackwardGraph";
  338. py_type.tp_basicsize = sizeof(PyOp(BackwardGraph));
  339. py_type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
  340. py_type.tp_doc = "BackwardGraph";
  341. py_type.tp_base = &PyOpType(OpDef);
  342. py_type.tp_dealloc = py_dealloc_generic<py_op>;
  343. py_type.tp_new = py_new_generic<py_op>;
  344. mgb_assert(PyType_Ready(&py_type) >= 0);
  345. // FIXME: rewrite interpret function in cpython instead wrap directly by pybind11::cppfunction
  346. auto interpret = py::cpp_function(
  347. [](OpDef& self, py::object pyf, py::object pyc,
  348. const mgb::SmallVector<py::object>& inputs) {
  349. auto f = [pyf](OpDef& op, const mgb::SmallVector<py::object>& inputs) {
  350. return py::cast<mgb::SmallVector<py::object>>(pyf(op.shared_from_this(), inputs));
  351. };
  352. auto c = [pyc](const TensorPtr& tensor) {
  353. return pyc(tensor->dev_tensor());
  354. };
  355. return self.cast_final_safe<BackwardGraph>().graph().interpret<py::object>(f, c, inputs);
  356. });
  357. mgb_assert(PyDict_SetItemString(
  358. py_type.tp_dict, "interpret", interpret.release().ptr()) >= 0);
  359. PyType_Modified(&py_type);
  360. m.add_object("BackwardGraph", reinterpret_cast<PyObject*>(&py_type));
  361. mgb_assert(PyOp(OpDef)::ctype2pytype.emplace(BackwardGraph::typeinfo(), &py_type).second);
  362. }
  363. struct PyOpBase : PyOpDef {
  364. static PyTypeObject py_type;
  365. static PyObject* tp_new(PyTypeObject* type, PyObject*, PyObject*) {
  366. auto* obj = type->tp_alloc(type, 0);
  367. if (obj) {
  368. auto* self = reinterpret_cast<PyOpBase*>(obj);
  369. new(&self->op) decltype(self->op);
  370. }
  371. return obj;
  372. }
  373. };
  374. PyTypeObject PyOpBase::py_type;
  375. void _init_py_op_base(py::module m) {
  376. using py_op = PyOpBase;
  377. auto& py_type = PyOpBase::py_type;
  378. py_type = {PyVarObject_HEAD_INIT(NULL, 0)};
  379. py_type.tp_name = "megengine.core._imperative_rt.ops.PyOpBase";
  380. py_type.tp_basicsize = sizeof(py_op);
  381. py_type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
  382. py_type.tp_doc = "PyOpBase";
  383. py_type.tp_base = &PyOpType(OpDef);
  384. py_type.tp_dealloc = py_dealloc_generic<py_op>;
  385. py_type.tp_new = py_op::tp_new;
  386. mgb_assert(PyType_Ready(&py_type) >= 0);
  387. m.add_object("PyOpBase", reinterpret_cast<PyObject*>(&py_type));
  388. }
  389. /*********** end of hand-write opdefs **************/
  390. // auto generated opdefs
  391. #include "opdef.cpy.inl"
  392. #undef CATCH_ALL
  393. } // anonymous namespace
  394. namespace PYBIND11_NAMESPACE {
  395. namespace detail {
  396. bool type_caster<OpDef>::load(handle src, bool convert) {
  397. PyObject* obj = src.ptr();
  398. if (!PyObject_TypeCheck(obj, &PyOpType(OpDef))) {
  399. return false;
  400. }
  401. value = reinterpret_cast<PyOp(OpDef)*>(obj)->op;
  402. if (!value) {
  403. // opdef only defined in Python
  404. value = std::make_shared<GenericPyOp>(reinterpret_borrow<object>(src));
  405. }
  406. return true;
  407. }
  408. handle type_caster<OpDef>::cast(const OpDef& op, return_value_policy, handle) {
  409. if (auto* pyop = op.try_cast_final<GenericPyOp>()) {
  410. return object(pyop->obj).release();
  411. }
  412. PyTypeObject* pytype;
  413. auto& c2p = PyOp(OpDef)::ctype2pytype;
  414. auto&& iter = c2p.find(op.dyn_typeinfo());
  415. if (iter != c2p.end()) { // FIXME: should always meet this condition
  416. pytype = iter->second;
  417. } else { // which means unregistered op type, jsut make it as an opaque op type
  418. // currently, only OprAttr goes into this branch
  419. pytype = &PyOpType(OpDef);
  420. }
  421. PyObject* obj = pytype->tp_alloc(pytype, 0);
  422. mgb_assert(PyObject_TypeCheck(obj, &PyOpType(OpDef)));
  423. reinterpret_cast<PyOp(OpDef)*>(obj)->op = const_cast<OpDef&>(op).shared_from_this();
  424. return py::handle(obj);
  425. }
  426. } // detail
  427. } // PYBIND11_NAMESPACE
  428. void init_ops(py::module m) {
  429. _init_py_op_def(m);
  430. _init_py_backward_graph(m);
  431. _init_py_op_base(m);
  432. INIT_ALL_OP(m)
  433. }

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