| 413 | } |
| 414 | |
| 415 | std::optional<ValueRefList> reduce_grad_rule( |
| 416 | const OpDef& op, Span<ValueRef> inputs, Span<bool> inputs_require_grad, |
| 417 | CustomBackward& backward) { |
| 418 | auto& reduce = op.cast_final_safe<Reduce>(); |
| 419 | if (reduce.mode != Reduce::Mode::SUM) { |
| 420 | return {}; |
| 421 | } |
| 422 | auto axis = reduce.axis; |
| 423 | if (inputs.size() != 1 || axis == INT_MAX) { |
| 424 | return {}; |
| 425 | } |
| 426 | std::array<ValueRef, 1> input_shapes; |
| 427 | if (inputs_require_grad[0]) { |
| 428 | input_shapes[0] = get_shape(inputs[0]); |
| 429 | } |
| 430 | if (axis < 0) { |
| 431 | axis = (*inputs[0].shape()).ndim + axis; |
| 432 | } |
| 433 | auto maker = CustomGradMaker(backward, inputs.size()); |
| 434 | auto keepdim = reduce.keepdim || axis == INT_MAX; |
| 435 | maker.output_size(1).output_captured(0, false); |
| 436 | maker.backward( |
| 437 | [shapes = std::move(input_shapes), axis, keepdim](Span<ValueRef> grads) { |
| 438 | mgb_assert(grads.size() == 1); |
| 439 | ValueRef grad = grads[0]; |
| 440 | if (!keepdim && grad) { |
| 441 | auto&& grad_op = AddAxis::make(std::vector<int32_t>({axis})); |
| 442 | grad = imperative::apply(*grad_op, grad)[0]; |
| 443 | } |
| 444 | SmallVector<ValueRef> ret(1); |
| 445 | if (grad && shapes[0]) { |
| 446 | ret[0] = broadcast_to(grad, shapes[0]); |
| 447 | } |
| 448 | return ret; |
| 449 | }); |
| 450 | maker.finalize(); |
| 451 | return imperative::apply(ApplyOp(op), inputs); |
| 452 | } |
| 453 | |
| 454 | std::optional<ValueRefList> addAxis_grad_rule( |
| 455 | const OpDef& op, Span<ValueRef> inputs, Span<bool> inputs_require_grad, |
nothing calls this directly
no test coverage detected