| 218 | } |
| 219 | |
| 220 | std::optional<ValueRefList> elemwise_grad_rule( |
| 221 | const OpDef& op, Span<ValueRef> inputs, Span<bool> inputs_require_grad, |
| 222 | CustomBackward& backward) { |
| 223 | auto& elemwise = op.cast_final_safe<Elemwise>(); |
| 224 | if (elemwise.mode != Elemwise::Mode::ADD) { |
| 225 | return {}; |
| 226 | } |
| 227 | mgb_assert(inputs.size() == 2); |
| 228 | std::array<ValueRef, 2> input_shapes; |
| 229 | for (size_t i = 0; i < 2; ++i) { |
| 230 | if (inputs_require_grad[i]) { |
| 231 | input_shapes[i] = get_shape(inputs[i]); |
| 232 | } |
| 233 | } |
| 234 | auto maker = CustomGradMaker(backward, inputs.size()); |
| 235 | maker.output_size(1).output_captured(0, false); |
| 236 | maker.backward([shapes = std::move(input_shapes)](Span<ValueRef> grads) { |
| 237 | mgb_assert(grads.size() == 1); |
| 238 | ValueRef grad = grads[0]; |
| 239 | SmallVector<ValueRef> ret(2); |
| 240 | if (!grad) { |
| 241 | return ret; |
| 242 | } |
| 243 | for (size_t i = 0; i < 2; ++i) { |
| 244 | if (shapes[i]) { |
| 245 | ret[i] = reduce_to(grad, shapes[i]); |
| 246 | } |
| 247 | } |
| 248 | return ret; |
| 249 | }); |
| 250 | maker.finalize(); |
| 251 | return imperative::apply(ApplyOp(op), inputs); |
| 252 | } |
| 253 | |
| 254 | std::optional<ValueRefList> reshape_grad_rule( |
| 255 | const OpDef& op, Span<ValueRef> inputs, Span<bool> inputs_require_grad, |
nothing calls this directly
no test coverage detected