| 23 | GradKeyWrapper::GradKeyWrapper() {} |
| 24 | |
| 25 | void GradKeyWrapper::attach(PyObject* const* args, size_t nargs) { |
| 26 | if (nargs != 2) { |
| 27 | throw py::type_error("expect 2 arguments"); |
| 28 | } |
| 29 | auto* tw = TensorWrapper::try_cast(args[0]); |
| 30 | if (!tw) { |
| 31 | throw py::type_error("argument 1 must be Tensor"); |
| 32 | } |
| 33 | py::object callback; |
| 34 | if (args[1] != Py_None) { |
| 35 | callback = py::reinterpret_borrow<py::object>(args[1]); |
| 36 | } |
| 37 | GenericFunction generic_callback = [=](Span<ValueRef> inputs) -> ValueRefList { |
| 38 | mgb_assert(inputs.size() == 1); |
| 39 | if (callback) { |
| 40 | callback(TensorWrapper::make(py_tensor_type, inputs[0])); |
| 41 | } |
| 42 | return {}; |
| 43 | }; |
| 44 | auto attached_value = imperative::apply( |
| 45 | AttachGrad(m_key), tw->m_tensor->data(), |
| 46 | FunctionValue::make(generic_callback))[0]; |
| 47 | tw->m_tensor->reset(attached_value); |
| 48 | } |
| 49 | |
| 50 | void GradKeyWrapper::backward(GradKeyWrapper* self, py::list tensors, py::list grads) { |
| 51 | std::vector<ValueRef> args; |