| 283 | } |
| 284 | |
| 285 | std::optional<ValueRefList> broadcast_grad_rule( |
| 286 | const OpDef& op, Span<ValueRef> inputs, Span<bool> inputs_require_grad, |
| 287 | CustomBackward& backward) { |
| 288 | mgb_assert(inputs.size() == 1 || inputs.size() == 2); |
| 289 | size_t nr_inp = inputs.size(); |
| 290 | std::array<ValueRef, 2> input_shapes; |
| 291 | for (size_t i = 0; i < nr_inp; ++i) { |
| 292 | if (inputs_require_grad[i]) { |
| 293 | input_shapes[i] = get_shape(inputs[i]); |
| 294 | } |
| 295 | } |
| 296 | auto maker = CustomGradMaker(backward, inputs.size()); |
| 297 | maker.output_size(1).output_captured(0, false); |
| 298 | maker.backward([shapes = std::move(input_shapes), nr_inp](Span<ValueRef> grads) { |
| 299 | mgb_assert(grads.size() == 1); |
| 300 | ValueRef grad = grads[0]; |
| 301 | SmallVector<ValueRef> ret(nr_inp); |
| 302 | if (!grad) { |
| 303 | return ret; |
| 304 | } |
| 305 | for (size_t i = 0; i < nr_inp; ++i) { |
| 306 | if (shapes[i]) { |
| 307 | ret[i] = reduce_to(grad, shapes[i]); |
| 308 | } |
| 309 | } |
| 310 | return ret; |
| 311 | }); |
| 312 | maker.finalize(); |
| 313 | return imperative::apply(ApplyOp(op), inputs); |
| 314 | } |
| 315 | |
| 316 | std::optional<ValueRefList> subtensor_grad_rule( |
| 317 | const OpDef& op, Span<ValueRef> inputs, Span<bool> inputs_require_grad, |
nothing calls this directly
no test coverage detected