| 314 | } |
| 315 | |
| 316 | std::optional<ValueRefList> subtensor_grad_rule( |
| 317 | const OpDef& op, Span<ValueRef> inputs, Span<bool> inputs_require_grad, |
| 318 | CustomBackward& backward) { |
| 319 | auto&& subtensor = op.cast_final_safe<Subtensor>(); |
| 320 | auto&& grad_op = SetSubtensor::make(subtensor.items); |
| 321 | SmallVector<ValueRef> inputs2; |
| 322 | if (inputs_require_grad[0]) { |
| 323 | inputs2.push_back(get_shape(inputs[0])); |
| 324 | for (size_t i = 1; i < inputs.size(); ++i) { |
| 325 | inputs2.push_back(inputs[i]); |
| 326 | } |
| 327 | } |
| 328 | CompNodeValue::ref_t device = inputs[0].device(); |
| 329 | auto get_subtensor_index = [&](int idx) { |
| 330 | HostTensorStorage storage(*device); |
| 331 | storage.ensure_size(dtype::Int32().size()); |
| 332 | auto* ptr = reinterpret_cast<dt_int32*>(storage.ptr()); |
| 333 | ptr[0] = idx; |
| 334 | return imperative::apply( |
| 335 | CreateTensor( |
| 336 | CreateTensor::Unique, *device, dtype::Int32(), ValueShape({1})), |
| 337 | HostStorage::make(storage))[0]; |
| 338 | }; |
| 339 | auto slice_items = subtensor.slice_items; |
| 340 | auto items = subtensor.items; |
| 341 | for (int i = 0; i < slice_items.size(); i++) { |
| 342 | auto&& [axis, b_flag, e_flag, s_flag, idx_flag] = items[i]; |
| 343 | auto&& [b_val, e_val, s_val, ax_val] = slice_items[i]; |
| 344 | if (b_flag) { |
| 345 | inputs2.push_back(get_subtensor_index(b_val)); |
| 346 | }; |
| 347 | if (e_flag) { |
| 348 | inputs2.push_back(get_subtensor_index(e_val)); |
| 349 | }; |
| 350 | if (s_flag) { |
| 351 | inputs2.push_back(get_subtensor_index(s_val)); |
| 352 | }; |
| 353 | if (idx_flag) { |
| 354 | inputs2.push_back(get_subtensor_index(ax_val)); |
| 355 | }; |
| 356 | }; |
| 357 | auto maker = CustomGradMaker(backward, inputs.size()); |
| 358 | maker.output_size(1).output_captured(0, false); |
| 359 | maker.backward([inputs = std::move(inputs2), |
| 360 | grad_op_ = std::move(grad_op)](Span<ValueRef> grads) { |
| 361 | mgb_assert(grads.size() == 1); |
| 362 | ValueRef grad = grads[0]; |
| 363 | SmallVector<ValueRef> ret(1); |
| 364 | if (grad && inputs[0]) { |
| 365 | ValueRefList args_(inputs.size() + 1); |
| 366 | auto&& zeros = make_empty_tensor(grad.device(), inputs[0], grad.dtype()); |
| 367 | args_[0] = zeros; |
| 368 | args_[1] = grad; |
| 369 | for (size_t i = 1; i < inputs.size(); ++i) { |
| 370 | args_[i + 1] = inputs[i]; |
| 371 | } |
| 372 | ret[0] = imperative::apply(ApplyOp(*grad_op_), args_)[0]; |
| 373 | } |
nothing calls this directly
no test coverage detected