// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include "paddle/phi/infermeta/binary.h" #include "paddle/phi/kernels/funcs/blas/blas.h" #include "paddle/phi/kernels/funcs/math_function.h" #include "paddle/phi/kernels/funcs/matrix_solve.h" #include "paddle/phi/kernels/impl/lu_kernel_impl.h" #include "paddle/phi/kernels/lu_solve_grad_kernel.h" #include "paddle/phi/kernels/lu_solve_kernel.h" #include "paddle/phi/kernels/lu_unpack_kernel.h" #include "paddle/phi/kernels/triangular_solve_kernel.h" namespace phi { template DenseTensor GetMH(const Context& dev_ctx, const DenseTensor x) { DenseTensor x_mH; Tensor_Conj(dev_ctx, x, &x_mH); return Transpose2DTo6D(dev_ctx, x_mH); } template void LuSolveGradKernel(const Context& dev_ctx, const DenseTensor& b, const DenseTensor& lu, const DenseTensor& pivots, const DenseTensor& out, const DenseTensor& out_grad, const std::string& trans, DenseTensor* b_grad, DenseTensor* lu_grad) { if (b_grad != nullptr) { dev_ctx.template Alloc(b_grad); std::string trans_t = (trans == "N") ? "T" : "N"; LuSolveKernel(dev_ctx, out_grad, lu, pivots, trans_t, b_grad); } if (lu_grad != nullptr) { dev_ctx.template Alloc(lu_grad); DenseTensor p, l, u, l_mH, u_mH; MetaTensor meta_p(&p); MetaTensor meta_l(&l); MetaTensor meta_u(&u); bool unpack_pivots = (trans == "N") ? false : true; LUUnpackInferMeta( lu, pivots, true, unpack_pivots, &meta_p, &meta_l, &meta_u); LUUnpackKernel( dev_ctx, lu, pivots, true, unpack_pivots, &p, &l, &u); l_mH = GetMH(dev_ctx, l); u_mH = GetMH(dev_ctx, u); if (trans == "N") { // gR = U^{-H}op_2(-gX)op_2(X)^Ha DenseTensor gR, psi_tmp, out_mH; out_mH = GetMH(dev_ctx, out); auto blas = funcs::GetBlas(dev_ctx); auto out_grad_dims = out_grad.dims(); auto mat_dim_l = funcs::CreateMatrixDescriptor(out_grad_dims, 0, false); auto out_mH_dims = out_mH.dims(); auto mat_dim_g = funcs::CreateMatrixDescriptor(out_mH_dims, 0, false); psi_tmp.Resize(lu.dims()); dev_ctx.template Alloc(&psi_tmp); blas.MatMul(out_grad, mat_dim_l, out_mH, mat_dim_g, static_cast(-1), &psi_tmp, static_cast(0)); TriangularSolveKernel( dev_ctx, u_mH, psi_tmp, false, false, false, &gR); // gL = (L^{-H} gR U^H).tril(-1) DenseTensor mul_tmp, gL; auto gr_dims = gR.dims(); auto mat_dim_r = funcs::CreateMatrixDescriptor(gr_dims, 0, false); auto gu_dims = u_mH.dims(); auto mat_dim_u = funcs::CreateMatrixDescriptor(gu_dims, 0, false); mul_tmp.Resize(gr_dims); dev_ctx.template Alloc(&mul_tmp); blas.MatMul(gR, mat_dim_r, u_mH, mat_dim_u, static_cast(1), &mul_tmp, static_cast(0)); TriangularSolveKernel( dev_ctx, l_mH, mul_tmp, true, false, true, &gL); auto phil_rank = gL.dims().size(); auto phir_rank = gR.dims().size(); funcs::ForRange l_for_range(dev_ctx, gL.numel()); funcs::TrilTriuCompute tril_computer(gL.data(), -1, true, gL.dims()[phil_rank - 2], gL.dims()[phil_rank - 1], gL.data()); l_for_range(tril_computer); funcs::ForRange r_for_range(dev_ctx, gR.numel()); funcs::TrilTriuCompute triu_computer(gR.data(), 0, false, gR.dims()[phir_rank - 2], gR.dims()[phir_rank - 1], gR.data()); r_for_range(triu_computer); Tensor_Add(dev_ctx, gL, gR, lu_grad); } else { DenseTensor gR, p_mT, tem_out, out_grad_mH, tem_out1, tem_out2, tem_out3, gU; p_mT = Transpose2DTo6D(dev_ctx, p); auto PmTdims = p_mT.dims(); auto Outdims = out.dims(); auto mat_dim_p = funcs::CreateMatrixDescriptor(PmTdims, 0, false); auto mat_dim_o = funcs::CreateMatrixDescriptor(Outdims, 0, false); tem_out.Resize(Outdims); dev_ctx.template Alloc(&tem_out); auto blas = funcs::GetBlas(dev_ctx); // gR = -P^T op_3(X)op_1(op_2(gX))P blas.MatMul(p_mT, mat_dim_p, out, mat_dim_o, static_cast(-1), &tem_out, static_cast(0)); out_grad_mH = GetMH(dev_ctx, out_grad); auto TemOutdims = tem_out.dims(); auto OutGradmHdims = out_grad_mH.dims(); auto mat_dim_tem_out = funcs::CreateMatrixDescriptor(TemOutdims, 0, false); auto mat_dim_out_grad_mH = funcs::CreateMatrixDescriptor(OutGradmHdims, 0, false); tem_out1.Resize(lu.dims()); dev_ctx.template Alloc(&tem_out1); blas.MatMul(tem_out, mat_dim_tem_out, out_grad_mH, mat_dim_out_grad_mH, static_cast(1), &tem_out1, static_cast(0)); auto TemOutdims1 = tem_out1.dims(); auto pdims = p.dims(); auto mat_dim_tem_out1 = funcs::CreateMatrixDescriptor(TemOutdims1, 0, false); auto mat_dim_p1 = funcs::CreateMatrixDescriptor(pdims, 0, false); tem_out2.Resize(TemOutdims1); dev_ctx.template Alloc(&tem_out2); blas.MatMul(tem_out1, mat_dim_tem_out1, p, mat_dim_p1, static_cast(1), &tem_out2, static_cast(0)); // gR = gR L^{-H} TriangularSolveKernel( dev_ctx, l_mH, tem_out2, true, true, true, &gR); // gU = (L^H gR U^{-H}).triu() auto LmHdims = l_mH.dims(); auto gRdims = gR.dims(); auto mat_dim_l_mh = funcs::CreateMatrixDescriptor(LmHdims, 0, false); auto mat_dim_gr = funcs::CreateMatrixDescriptor(gRdims, 0, false); tem_out3.Resize(LmHdims); dev_ctx.template Alloc(&tem_out3); blas.MatMul(l_mH, mat_dim_l_mh, gR, mat_dim_gr, static_cast(1), &tem_out3, static_cast(0)); TriangularSolveKernel( dev_ctx, u_mH, tem_out3, false, true, false, &gU); auto phiu_rank = gU.dims().size(); auto phir_rank = gR.dims().size(); funcs::ForRange l_for_range(dev_ctx, gR.numel()); funcs::TrilTriuCompute tril_computer(gR.data(), -1, true, gR.dims()[phir_rank - 2], gR.dims()[phir_rank - 1], gR.data()); l_for_range(tril_computer); funcs::ForRange r_for_range(dev_ctx, gU.numel()); funcs::TrilTriuCompute triu_computer(gU.data(), 0, false, gU.dims()[phiu_rank - 2], gU.dims()[phiu_rank - 1], gU.data()); r_for_range(triu_computer); Tensor_Add(dev_ctx, gR, gU, lu_grad); } } } } // namespace phi