// Copyright (c) 2022 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. #include #include #include #include "paddle/common/enforce.h" #include "paddle/phi/backends/gpu/cuda/cudnn_workspace_helper.h" #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/full_kernel.h" #include "paddle/phi/kernels/funcs/slice.h" #include "paddle/phi/kernels/impl/lstsq_kernel_impl.h" #include "paddle/phi/kernels/impl/qr_kernel_impl.h" #include "paddle/phi/kernels/impl/tril_triu_kernel_impl.h" #include "paddle/phi/kernels/lstsq_kernel.h" #include "paddle/phi/kernels/matmul_kernel.h" #include "paddle/phi/kernels/transpose_kernel.h" #include "paddle/phi/kernels/triangular_solve_kernel.h" namespace phi { enum class LapackDriverType : int { Gels, Gelsd, Gelsy, Gelss }; template void LstsqKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& y, const Scalar& rcond_scalar, const std::string& driver_string, DenseTensor* solution, DenseTensor* residuals, DenseTensor* rank, DenseTensor* singular_values) { if (x.numel() == 0 || y.numel() == 0) { if (solution) Full(dev_ctx, solution->dims(), 0, solution); if (rank) Full(dev_ctx, rank->dims(), 0, rank); if (residuals) GetResidualsTensor( dev_ctx, x, y, driver_string, solution, residuals, rank); if (singular_values) Full(dev_ctx, singular_values->dims(), 0, singular_values); return; } CUDNN_ENFORCE_TENSOR_SIZE_SUPPORTED(x); CUDNN_ENFORCE_TENSOR_SIZE_SUPPORTED(y); auto x_dims = x.dims(); auto y_dims = y.dims(); int dim_size = x_dims.size(); const int64_t m_64 = x_dims[dim_size - 2]; const int64_t n_64 = x_dims[dim_size - 1]; const int64_t nrhs_64 = y_dims[dim_size - 1]; PADDLE_ENFORCE_LE_INT_MAX(m_64, "lstsq m"); PADDLE_ENFORCE_LE_INT_MAX(n_64, "lstsq n"); PADDLE_ENFORCE_LE_INT_MAX(nrhs_64, "lstsq nrhs"); const int m = static_cast(m_64); const int n = static_cast(n_64); const int nrhs = static_cast(nrhs_64); int min_mn = std::min(m, n); int max_mn = std::max(m, n); int k = min_mn; int x_stride = GetMatrixStride(x_dims); int y_stride = GetMatrixStride(y_dims); int tau_stride = min_mn; int batch_count = GetBatchCount(x_dims); T rcond = rcond_scalar.to(); DenseTensor new_x; new_x.Resize({batch_count, m, n}); dev_ctx.template Alloc(&new_x); Copy(dev_ctx, x, dev_ctx.GetPlace(), true, &new_x); DenseTensor new_y; new_y.Resize({batch_count, m, nrhs}); dev_ctx.template Alloc(&new_y); Copy(dev_ctx, y, dev_ctx.GetPlace(), true, &new_y); // Prepare tau auto tau_dims_vec = vectorize(x_dims); tau_dims_vec.pop_back(); tau_dims_vec[tau_dims_vec.size() - 1] = min_mn; DenseTensor tau; tau.Resize(tau_dims_vec); auto tau_data = dev_ctx.template Alloc(&tau); if (m >= n) { DenseTensor tmp_x = TransposeLast2Dim(dev_ctx, new_x); DenseTensor tmp_y = TransposeLast2Dim(dev_ctx, new_y); auto x_data = tmp_x.data(); auto y_data = tmp_y.data(); // step 1, compute QR factorization using geqrf BatchedGeqrf( dev_ctx, batch_count, m, n, x_data, m, tau_data, x_stride, tau_stride); // Step 2, Y <- Q^H Y BatchedOrmqr(dev_ctx, true, true, batch_count, m, nrhs, k, x_data, x_stride, tau_data, tau_stride, y_data, y_stride); DenseTensor trans_r = TransposeLast2Dim(dev_ctx, tmp_x); DenseTensor slice_r = funcs::Slice(dev_ctx, trans_r, {-2}, {0}, {min_mn}); DenseTensor res_r; res_r.Resize({batch_count, min_mn, min_mn}); dev_ctx.template Alloc(&res_r); TrilTriuKernel(dev_ctx, slice_r, 0, false, &res_r); DenseTensor trans_y = TransposeLast2Dim(dev_ctx, tmp_y); DenseTensor slice_y = funcs::Slice(dev_ctx, trans_y, {-2}, {0}, {min_mn}); // Step 3, solve R X = Y TriangularSolveKernel( dev_ctx, res_r, slice_y, true, false, false, solution); } else { auto x_data = dev_ctx.template Alloc(&new_x); auto y_data = dev_ctx.template Alloc(&new_y); // step 1, compute QR factorization using geqrf BatchedGeqrf( dev_ctx, batch_count, n, m, x_data, n, tau_data, x_stride, tau_stride); // Step 2, solve R^H Z = Y DenseTensor trans_r = TransposeLast2Dim(dev_ctx, new_x); DenseTensor slice_r = funcs::Slice(dev_ctx, trans_r, {-2}, {0}, {min_mn}); DenseTensor res_r; res_r.Resize({batch_count, min_mn, min_mn}); dev_ctx.template Alloc(&res_r); TrilTriuKernel(dev_ctx, slice_r, 0, false, &res_r); TriangularSolveKernel( dev_ctx, res_r, new_y, true, true, false, solution); // Step 3, X <- Q Z BatchedOrgqr(dev_ctx, batch_count, n, m, min_mn, x_data, n, tau_data, x_stride, tau_stride); DenseTensor trans_q = TransposeLast2Dim(dev_ctx, new_x); DenseTensor slice_q = funcs::Slice(dev_ctx, trans_q, {-1}, {0}, {m}); DenseTensor solu_tensor = Matmul(dev_ctx, slice_q, *solution, false, false); Copy(dev_ctx, solu_tensor, dev_ctx.GetPlace(), true, solution); } if (batch_count == 1) solution->Resize({n, nrhs}); GetResidualsTensor( dev_ctx, x, y, driver_string, solution, residuals, rank); } } // namespace phi PD_REGISTER_KERNEL(lstsq, GPU, ALL_LAYOUT, phi::LstsqKernel, float, double) {}