| 830 | } |
| 831 | |
| 832 | Maybe<Tensor> Diagonal(const std::shared_ptr<Tensor>& input, const int32_t offset, |
| 833 | const int32_t dim1, const int32_t dim2) { |
| 834 | const auto& shape = input->shape(); |
| 835 | const auto& stride = JUST(input->stride()); |
| 836 | const int64_t ndim = shape->NumAxes(); |
| 837 | int64_t storage_offset = JUST(JUST(input->AsLocalTensor())->storage_offset()); |
| 838 | |
| 839 | // infer output storage_offset |
| 840 | int64_t diag_size = 0; |
| 841 | if (offset >= 0) { |
| 842 | diag_size = std::max<int64_t>(std::min(shape->At(dim1), shape->At(dim2) - offset), 0); |
| 843 | } else { |
| 844 | diag_size = std::max<int64_t>(std::min(shape->At(dim1) + offset, shape->At(dim2)), 0); |
| 845 | } |
| 846 | if (diag_size == 0) { |
| 847 | // skip |
| 848 | } else if (offset >= 0) { |
| 849 | storage_offset += offset * stride->at(dim2); |
| 850 | } else { |
| 851 | storage_offset -= offset * stride->at(dim1); |
| 852 | } |
| 853 | |
| 854 | CHECK_GE_OR_RETURN(ndim, 2) << "input tensor's ndim should be >= 2, but got " << ndim; |
| 855 | // infer output shape and stride |
| 856 | DimVector out_shape(shape->dim_vec()); |
| 857 | Stride out_stride(*stride); |
| 858 | out_shape.erase(out_shape.begin() + std::max(dim1, dim2)); |
| 859 | out_stride.erase(out_stride.begin() + std::max(dim1, dim2)); |
| 860 | out_shape.erase(out_shape.begin() + std::min(dim1, dim2)); |
| 861 | out_stride.erase(out_stride.begin() + std::min(dim1, dim2)); |
| 862 | out_shape.emplace_back(diag_size); |
| 863 | out_stride.emplace_back(stride->at(dim1) + stride->at(dim2)); |
| 864 | |
| 865 | // generate view tensor |
| 866 | auto output = JUST(BasicView(input, Shape(out_shape), out_stride, storage_offset)); |
| 867 | // autograd |
| 868 | if (autograd::GradMode::is_enabled() && input->requires_grad()) { |
| 869 | std::vector<int32_t> input_index{dim1, dim2}; |
| 870 | for (int32_t i = 0; i < ndim; i++) { |
| 871 | if (i != dim1 && i != dim2) { input_index.push_back(i); } |
| 872 | } |
| 873 | |
| 874 | auto backward_fn = std::make_shared<BackwardFunction>(); |
| 875 | backward_fn->body = [=](const TensorTuple& out_grads, TensorTuple* in_grads, |
| 876 | bool create_graph) -> Maybe<void> { |
| 877 | autograd::AutoGradMode mode(create_graph); |
| 878 | CHECK_EQ_OR_RETURN(out_grads.size(), 1) |
| 879 | << "out grad size should be 1, but got " << out_grads.size(); |
| 880 | in_grads->resize(1); |
| 881 | std::shared_ptr<one::Tensor> d_x = JUST(functional::Transpose(input, input_index)); |
| 882 | (*in_grads)[0] = JUST(functional::DiagonalGrad(out_grads[0], d_x, offset)); |
| 883 | return Maybe<void>::Ok(); |
| 884 | }; |
| 885 | backward_fn->status = []() { return true; }; |
| 886 | TensorTuple outputs{output}; |
| 887 | JUST(GetThreadLocalAutogradEngine()->AddNode("view::diagonal_backward", backward_fn, {input}, |
| 888 | &outputs)); |
| 889 | } |
no test coverage detected