// 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. #ifdef PADDLE_WITH_HIP #include "paddle/phi/backends/dynload/rocsolver.h" #else #include "paddle/phi/backends/dynload/cusolver.h" #endif #include #include #include #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/common/memory_utils.h" #include "paddle/phi/core/enforce.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/infermeta/unary.h" #include "paddle/phi/kernels/diagonal_kernel.h" #include "paddle/phi/kernels/fill_diagonal_tensor_kernel.h" #include "paddle/phi/kernels/funcs/complex_functors.h" #include "paddle/phi/kernels/funcs/math_function.h" #include "paddle/phi/kernels/funcs/parse_qr_mode.h" #include "paddle/phi/kernels/impl/qr_kernel_impl.h" #include "paddle/phi/kernels/qr_kernel.h" #include "paddle/phi/kernels/slice_kernel.h" #include "paddle/phi/kernels/transpose_kernel.h" #include "paddle/phi/kernels/tril_triu_kernel.h" namespace phi { template static DenseTensor Fill(const Context& dev_ctx, std::vector shape, T fill_value) { DenseTensor ret; ret.Resize(shape); dev_ctx.template Alloc(&ret); funcs::SetConstant()(dev_ctx, &ret, fill_value); return ret; } template static DenseTensor identity_matrix(const Context& dev_ctx, common::DDim shape) { DenseTensor M = Fill(dev_ctx, vectorize(shape), T(0)); size_t rank = M.dims().size(); int64_t M_diag_len = std::min(M.dims()[rank - 1], M.dims()[rank - 2]); std::vector M_diag_shape; for (size_t i = 0; i < rank - 2; ++i) { M_diag_shape.push_back(M.dims()[i]); } M_diag_shape.push_back(M_diag_len); DenseTensor M_diag = Fill( dev_ctx, vectorize(make_ddim(M_diag_shape)), T(1)); M = FillDiagonalTensor(dev_ctx, M, M_diag, 0, rank - 2, rank - 1); return M; } template struct QrFunctor { void operator()(const Context& dev_ctx, const DenseTensor& x, bool compute_q, bool reduced_mode, DenseTensor* q, DenseTensor* r) { auto x_dims = x.dims(); int x_rank = x_dims.size(); int m = x_dims[x_rank - 2]; int n = x_dims[x_rank - 1]; int min_mn = std::min(m, n); int k = reduced_mode ? min_mn : m; int64_t batch_size = static_cast(x.numel() / (static_cast(m) * n)); int64_t qr_stride = static_cast(m) * n; int tau_stride = min_mn; if (compute_q) { dev_ctx.template Alloc>( q, batch_size * m * k * sizeof(dtype::Real)); } dev_ctx.template Alloc>( r, batch_size * k * n * sizeof(dtype::Real)); // Note: allocate temporary tensors because of lacking in-place operations. // Prepare qr DenseTensor qr; dev_ctx.template Alloc>( &qr, size_t(batch_size * m * n * sizeof(dtype::Real))); // BatchedGeqrf performs computation in-place and 'qr' must be a copy of // input Copy(dev_ctx, x, dev_ctx.GetPlace(), false, &qr); // 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 = Fill(dev_ctx, tau_dims_vec, T(0)); // Transpose 'qr' to conform the column-major order auto tmp_qr = TransposeLast2Dim(dev_ctx, qr); Copy(dev_ctx, tmp_qr, qr.place(), false, &qr); auto qr_data = dev_ctx.template Alloc>(&qr); auto tau_data = dev_ctx.template Alloc>(&tau); BatchedGeqrf( dev_ctx, batch_size, m, n, qr_data, m, tau_data, qr_stride, tau_stride); if (reduced_mode) { auto trans_qr = TransposeLast2Dim(dev_ctx, qr); auto sliced_qr = Slice( dev_ctx, trans_qr, {trans_qr.dims().size() - 2}, {0}, {min_mn}); auto tmp_r = TrilTriu(dev_ctx, sliced_qr, 0, false); // Transpose 'tmp_r' to restore the original row-major order Copy(dev_ctx, tmp_r, r->place(), false, r); } else { auto trans_qr = TransposeLast2Dim(dev_ctx, qr); auto tmp_r = TrilTriu(dev_ctx, trans_qr, 0, false); // Transpose 'tmp_r' to restore the original row-major order Copy(dev_ctx, tmp_r, r->place(), false, r); } if (compute_q) { // Perform QRGQR for Q using the result from GEQRF // Transpose 'q' to restore the original row-major order if (reduced_mode) { BatchedOrgqr(dev_ctx, batch_size, m, min_mn, min_mn, qr_data, m, tau_data, qr_stride, tau_stride); auto trans_q = TransposeLast2Dim(dev_ctx, qr); auto sliced_q = Slice( dev_ctx, trans_q, {trans_q.dims().size() - 1}, {0}, {min_mn}); Copy(dev_ctx, sliced_q, q->place(), false, q); } else { if (m > n) { auto new_qr_dims_vec = vectorize(x_dims); new_qr_dims_vec[new_qr_dims_vec.size() - 1] = m; DenseTensor new_qr = Fill(dev_ctx, new_qr_dims_vec, T(0)); auto new_qr_data = dev_ctx.template Alloc>(&new_qr); auto new_qr_stride = m * m; for (int i = 0; i < batch_size; ++i) { memory_utils::Copy(dev_ctx.GetPlace(), (new_qr_data + i * new_qr_stride), dev_ctx.GetPlace(), (qr_data + i * qr_stride), qr_stride * sizeof(dtype::Real), dev_ctx.stream()); } BatchedOrgqr(dev_ctx, batch_size, m, m, min_mn, new_qr_data, m, tau_data, new_qr_stride, tau_stride); auto trans_q = TransposeLast2Dim(dev_ctx, new_qr); Copy(dev_ctx, trans_q, q->place(), false, q); } else { BatchedOrgqr(dev_ctx, batch_size, m, m, min_mn, qr_data, m, tau_data, qr_stride, tau_stride); auto trans_q = TransposeLast2Dim(dev_ctx, qr); auto sliced_q = Slice( dev_ctx, trans_q, {trans_q.dims().size() - 1}, {0}, {m}); Copy(dev_ctx, sliced_q, q->place(), false, q); } } } } }; template struct QrFunctor, Context> { void operator()(const Context& dev_ctx, const DenseTensor& x, bool compute_q, bool reduced_mode, DenseTensor* q, DenseTensor* r) { auto x_dims = x.dims(); int x_rank = x_dims.size(); int m = x_dims[x_rank - 2]; int n = x_dims[x_rank - 1]; int min_mn = std::min(m, n); int k = reduced_mode ? min_mn : m; int64_t batch_size = x.numel() / (static_cast(m) * n); int64_t qr_stride = static_cast(m) * n; int tau_stride = min_mn; if (compute_q) { dev_ctx.template Alloc>( q, batch_size * m * k * sizeof(dtype::complex)); } dev_ctx.template Alloc>( r, batch_size * k * n * sizeof(dtype::complex)); // Note: allocate temporary tensors because of lacking in-place operations. // Prepare qr DenseTensor qr; dev_ctx.template Alloc>( &qr, size_t(batch_size * m * n * sizeof(dtype::complex))); // BatchedGeqrf performs computation in-place and 'qr' must be a copy of // input Copy(dev_ctx, x, dev_ctx.GetPlace(), false, &qr); // 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 = Fill, Context>(dev_ctx, tau_dims_vec, T(0)); // Transpose 'qr' to conform the column-major order auto tmp_qr = TransposeLast2Dim, Context>(dev_ctx, qr); Copy(dev_ctx, tmp_qr, qr.place(), false, &qr); auto qr_data = dev_ctx.template Alloc>(&qr); auto tau_data = dev_ctx.template Alloc>(&tau); BatchedGeqrf>( dev_ctx, batch_size, m, n, qr_data, m, tau_data, qr_stride, tau_stride); if (reduced_mode) { auto trans_qr = TransposeLast2Dim, Context>(dev_ctx, qr); auto sliced_qr = Slice, Context>( dev_ctx, trans_qr, {trans_qr.dims().size() - 2}, {0}, {min_mn}); auto tmp_r = TrilTriu, Context>(dev_ctx, sliced_qr, 0, false); // Transpose 'tmp_r' to restore the original row-major order Copy(dev_ctx, tmp_r, r->place(), false, r); } else { auto trans_qr = TransposeLast2Dim, Context>(dev_ctx, qr); auto tmp_r = TrilTriu, Context>(dev_ctx, trans_qr, 0, false); // Transpose 'tmp_r' to restore the original row-major order Copy(dev_ctx, tmp_r, r->place(), false, r); } if (compute_q) { // Perform QRGQR for Q using the result from GEQRF // Transpose 'q' to restore the original row-major order if (reduced_mode) { BatchedOrgqr>(dev_ctx, batch_size, m, min_mn, min_mn, qr_data, m, tau_data, qr_stride, tau_stride); auto trans_q = TransposeLast2Dim, Context>(dev_ctx, qr); auto sliced_q = Slice, Context>( dev_ctx, trans_q, {trans_q.dims().size() - 1}, {0}, {min_mn}); Copy(dev_ctx, sliced_q, q->place(), false, q); } else { if (m > n) { auto new_qr_dims_vec = vectorize(x_dims); new_qr_dims_vec[new_qr_dims_vec.size() - 1] = m; DenseTensor new_qr = Fill, Context>(dev_ctx, new_qr_dims_vec, T(0)); auto new_qr_data = dev_ctx.template Alloc>(&new_qr); auto new_qr_stride = m * m; for (int i = 0; i < batch_size; ++i) { memory_utils::Copy(dev_ctx.GetPlace(), (new_qr_data + i * new_qr_stride), dev_ctx.GetPlace(), (qr_data + i * qr_stride), qr_stride * sizeof(dtype::complex), dev_ctx.stream()); } BatchedOrgqr>(dev_ctx, batch_size, m, m, min_mn, new_qr_data, m, tau_data, new_qr_stride, tau_stride); auto trans_q = TransposeLast2Dim, Context>(dev_ctx, new_qr); Copy(dev_ctx, trans_q, q->place(), false, q); } else { BatchedOrgqr>(dev_ctx, batch_size, m, m, min_mn, qr_data, m, tau_data, qr_stride, tau_stride); auto trans_q = TransposeLast2Dim, Context>(dev_ctx, qr); auto sliced_q = Slice, Context>( dev_ctx, trans_q, {trans_q.dims().size() - 1}, {0}, {m}); Copy(dev_ctx, sliced_q, q->place(), false, q); } } } } }; template void QrKernel(const Context& dev_ctx, const DenseTensor& x, const std::string& mode, DenseTensor* q, DenseTensor* r) { bool compute_q; bool reduced_mode; std::tie(compute_q, reduced_mode) = funcs::ParseQrMode(mode); if (x.numel() == 0) { if (q->numel() == 0) { q->Resize(q->dims()); } else { *q = identity_matrix(dev_ctx, q->dims()); } r->Resize(r->dims()); dev_ctx.template Alloc(q); dev_ctx.template Alloc(r); return; } QrFunctor()(dev_ctx, x, compute_q, reduced_mode, q, r); } #ifdef PADDLE_WITH_HIP #define FUNC_WITH_TYPES(m) m(float, s) m(double, d) #define GEQRF_BATCH_INSTANCE(T, C) \ template <> \ void BatchedGeqrf(const GPUContext& dev_ctx, \ int batch_size, \ int m, \ int n, \ T* a, \ int lda, \ T* tau, \ int a_stride, \ int tau_stride) { \ auto handle = dev_ctx.cusolver_dn_handle(); \ for (int i = 0; i < batch_size; ++i) { \ T* a_working_ptr = &a[i * a_stride]; \ T* tau_working_ptr = &tau[i * tau_stride]; \ PADDLE_ENFORCE_GPU_SUCCESS(dynload::rocsolver_##C##geqrf( \ handle, m, n, a_working_ptr, lda, tau_working_ptr)); \ } \ } FUNC_WITH_TYPES(GEQRF_BATCH_INSTANCE); #define ORGQR_BATCH_INSTANCE(T, C) \ template <> \ void BatchedOrgqr(const GPUContext& dev_ctx, \ int batch_size, \ int m, \ int n, \ int k, \ T* a, \ int lda, \ T* tau, \ int a_stride, \ int tau_stride) { \ auto handle = dev_ctx.cusolver_dn_handle(); \ for (int i = 0; i < batch_size; ++i) { \ T* a_working_ptr = &a[i * a_stride]; \ T* tau_working_ptr = &tau[i * tau_stride]; \ PADDLE_ENFORCE_GPU_SUCCESS(dynload::rocsolver_##C##orgqr( \ handle, m, n, k, a_working_ptr, lda, tau_working_ptr)); \ } \ } FUNC_WITH_TYPES(ORGQR_BATCH_INSTANCE); #else template <> void BatchedGeqrf(const GPUContext& dev_ctx, int batch_size, int m, int n, float* a, int lda, float* tau, int a_stride, int tau_stride) { if (static_cast(m) * n * 171 > std::numeric_limits::max()) { const int64_t batch_size_64 = static_cast(batch_size); const int64_t m_64 = static_cast(m); const int64_t n_64 = static_cast(n); const int64_t lda_64 = static_cast(lda); const int64_t a_stride_64 = static_cast(a_stride); const int64_t tau_stride_64 = static_cast(tau_stride); auto handle = dev_ctx.cusolver_dn_handle(); size_t workspace_in_bytes_on_device = 0; size_t workspace_in_bytes_on_host = 0; PADDLE_ENFORCE_GPU_SUCCESS( dynload::cusolverDnXgeqrf_bufferSize(handle, nullptr, m_64, n_64, CUDA_R_32F, a, lda_64, CUDA_R_32F, tau, CUDA_R_32F, &workspace_in_bytes_on_device, &workspace_in_bytes_on_host)); DenseTensor device_workspace; device_workspace.Resize( make_ddim({static_cast(workspace_in_bytes_on_device)})); uint8_t* device_workspace_ptr = dev_ctx.template Alloc(&device_workspace); DenseTensor host_workspace; uint8_t* host_workspace_ptr = nullptr; if (workspace_in_bytes_on_host > 0) { host_workspace.Resize( make_ddim({static_cast(workspace_in_bytes_on_host)})); host_workspace_ptr = dev_ctx.template HostAlloc(&host_workspace); } DenseTensor info; info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int64_t i = 0; i < batch_size_64; ++i) { float* a_working_ptr = &a[i * a_stride_64]; float* tau_working_ptr = &tau[i * tau_stride_64]; PADDLE_ENFORCE_GPU_SUCCESS( dynload::cusolverDnXgeqrf(handle, nullptr, m_64, n_64, CUDA_R_32F, a_working_ptr, lda_64, CUDA_R_32F, tau_working_ptr, CUDA_R_32F, device_workspace_ptr, workspace_in_bytes_on_device, host_workspace_ptr, workspace_in_bytes_on_host, info_d)); int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver (64-bit) geqrf is not zero. [%d]", i, info_h)); } } else { int lwork = 0; auto handle = dev_ctx.cusolver_dn_handle(); PADDLE_ENFORCE_GPU_SUCCESS( dynload::cusolverDnSgeqrf_bufferSize(handle, m, n, a, lda, &lwork)); DenseTensor workspace = DenseTensor(); workspace.Resize({lwork}); float* workspace_ptr = dev_ctx.template Alloc(&workspace); DenseTensor info = DenseTensor(); info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int i = 0; i < batch_size; ++i) { float* a_working_ptr = &a[i * a_stride]; float* tau_working_ptr = &tau[i * tau_stride]; // compute geqrf PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnSgeqrf(handle, m, n, a_working_ptr, lda, tau_working_ptr, workspace_ptr, lwork, info_d)); // Do we need synchronized here? // check the error info int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver geqrf is not zero. [%d]", i, info_h)); } } } template <> void BatchedGeqrf(const GPUContext& dev_ctx, int batch_size, int m, int n, double* a, int lda, double* tau, int a_stride, int tau_stride) { int lwork = 0; auto handle = dev_ctx.cusolver_dn_handle(); PADDLE_ENFORCE_GPU_SUCCESS( dynload::cusolverDnDgeqrf_bufferSize(handle, m, n, a, lda, &lwork)); DenseTensor workspace = DenseTensor(); workspace.Resize({lwork}); double* workspace_ptr = dev_ctx.template Alloc(&workspace); DenseTensor info = DenseTensor(); info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int i = 0; i < batch_size; ++i) { double* a_working_ptr = &a[i * a_stride]; double* tau_working_ptr = &tau[i * tau_stride]; // compute geqrf PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnDgeqrf(handle, m, n, a_working_ptr, lda, tau_working_ptr, workspace_ptr, lwork, info_d)); // Do we need synchronized here? // check the error info int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver geqrf is not zero. [%d]", i, info_h)); } } template <> void BatchedGeqrf(const GPUContext& dev_ctx, int batch_size, int m, int n, complex64* a, int lda, complex64* tau, int a_stride, int tau_stride) { int lwork = 0; auto handle = dev_ctx.cusolver_dn_handle(); PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnCgeqrf_bufferSize( handle, m, n, reinterpret_cast(a), lda, &lwork)); DenseTensor workspace = DenseTensor(); workspace.Resize({lwork}); complex64* workspace_ptr = dev_ctx.template Alloc(&workspace); DenseTensor info = DenseTensor(); info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int i = 0; i < batch_size; ++i) { complex64* a_working_ptr = &a[i * a_stride]; complex64* tau_working_ptr = &tau[i * tau_stride]; // compute geqrf PADDLE_ENFORCE_GPU_SUCCESS( dynload::cusolverDnCgeqrf(handle, m, n, reinterpret_cast(a_working_ptr), lda, reinterpret_cast(tau_working_ptr), reinterpret_cast(workspace_ptr), lwork, info_d)); // Do we need synchronized here? // check the error info int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver geqrf is not zero. [%d]", i, info_h)); } } template <> void BatchedGeqrf(const GPUContext& dev_ctx, int batch_size, int m, int n, complex128* a, int lda, complex128* tau, int a_stride, int tau_stride) { int lwork = 0; auto handle = dev_ctx.cusolver_dn_handle(); PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnZgeqrf_bufferSize( handle, m, n, reinterpret_cast(a), lda, &lwork)); DenseTensor workspace = DenseTensor(); workspace.Resize({lwork}); complex128* workspace_ptr = dev_ctx.template Alloc(&workspace); DenseTensor info = DenseTensor(); info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int i = 0; i < batch_size; ++i) { complex128* a_working_ptr = &a[i * a_stride]; complex128* tau_working_ptr = &tau[i * tau_stride]; // compute geqrf PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnZgeqrf( handle, m, n, reinterpret_cast(a_working_ptr), lda, reinterpret_cast(tau_working_ptr), reinterpret_cast(workspace_ptr), lwork, info_d)); // Do we need synchronized here? // check the error info int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver geqrf is not zero. [%d]", i, info_h)); } } template <> void BatchedOrgqr(const GPUContext& dev_ctx, int batch_size, int m, int n, int k, float* a, int lda, float* tau, int a_stride, int tau_stride) { int lwork = 0; auto handle = dev_ctx.cusolver_dn_handle(); PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnSorgqr_bufferSize( handle, m, n, k, a, lda, tau, &lwork)); DenseTensor workspace = DenseTensor(); workspace.Resize({lwork}); float* workspace_ptr = dev_ctx.template Alloc(&workspace); DenseTensor info = DenseTensor(); info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int i = 0; i < batch_size; ++i) { float* a_working_ptr = &a[i * a_stride]; float* tau_working_ptr = &tau[i * tau_stride]; // compute orggr PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnSorgqr(handle, m, n, k, a_working_ptr, lda, tau_working_ptr, workspace_ptr, lwork, info_d)); // Do we need synchronized here? // check the error info int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver QR is not zero. [%d]", i, info_h)); } } template <> void BatchedOrgqr(const GPUContext& dev_ctx, int batch_size, int m, int n, int k, double* a, int lda, double* tau, int a_stride, int tau_stride) { int lwork = 0; auto handle = dev_ctx.cusolver_dn_handle(); PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnDorgqr_bufferSize( handle, m, n, k, a, lda, tau, &lwork)); DenseTensor workspace = DenseTensor(); workspace.Resize({lwork}); double* workspace_ptr = dev_ctx.template Alloc(&workspace); DenseTensor info = DenseTensor(); info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int i = 0; i < batch_size; ++i) { double* a_working_ptr = &a[i * a_stride]; double* tau_working_ptr = &tau[i * tau_stride]; // compute orggr PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnDorgqr(handle, m, n, k, a_working_ptr, lda, tau_working_ptr, workspace_ptr, lwork, info_d)); // Do we need synchronized here? // check the error info int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver QR is not zero. [%d]", i, info_h)); } } template <> void BatchedOrgqr(const GPUContext& dev_ctx, int batch_size, int m, int n, int k, complex64* a, int lda, complex64* tau, int a_stride, int tau_stride) { int lwork = 0; auto handle = dev_ctx.cusolver_dn_handle(); PADDLE_ENFORCE_GPU_SUCCESS( dynload::cusolverDnCungqr_bufferSize(handle, m, n, k, reinterpret_cast(a), lda, reinterpret_cast(tau), &lwork)); DenseTensor workspace = DenseTensor(); workspace.Resize({lwork}); complex64* workspace_ptr = dev_ctx.template Alloc(&workspace); DenseTensor info = DenseTensor(); info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int i = 0; i < batch_size; ++i) { complex64* a_working_ptr = &a[i * a_stride]; complex64* tau_working_ptr = &tau[i * tau_stride]; // compute orggr PADDLE_ENFORCE_GPU_SUCCESS( dynload::cusolverDnCungqr(handle, m, n, k, reinterpret_cast(a_working_ptr), lda, reinterpret_cast(tau_working_ptr), reinterpret_cast(workspace_ptr), lwork, info_d)); // Do we need synchronized here? // check the error info int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver QR is not zero. [%d]", i, info_h)); } } template <> void BatchedOrgqr(const GPUContext& dev_ctx, int batch_size, int m, int n, int k, complex128* a, int lda, complex128* tau, int a_stride, int tau_stride) { int lwork = 0; auto handle = dev_ctx.cusolver_dn_handle(); PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnZungqr_bufferSize( handle, m, n, k, reinterpret_cast(a), lda, reinterpret_cast(tau), &lwork)); DenseTensor workspace = DenseTensor(); workspace.Resize({lwork}); complex128* workspace_ptr = dev_ctx.template Alloc(&workspace); DenseTensor info = DenseTensor(); info.Resize({1}); int* info_d = dev_ctx.template Alloc(&info); for (int i = 0; i < batch_size; ++i) { complex128* a_working_ptr = &a[i * a_stride]; complex128* tau_working_ptr = &tau[i * tau_stride]; // compute orggr PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnZungqr( handle, m, n, k, reinterpret_cast(a_working_ptr), lda, reinterpret_cast(tau_working_ptr), reinterpret_cast(workspace_ptr), lwork, info_d)); // Do we need synchronized here? // check the error info int info_h; memory_utils::Copy(CPUPlace(), &info_h, dev_ctx.GetPlace(), info_d, sizeof(int), dev_ctx.stream()); PADDLE_ENFORCE_EQ( info_h, 0, common::errors::PreconditionNotMet( "For batch [%d]: CUSolver QR is not zero. [%d]", i, info_h)); } } #endif } // namespace phi #ifdef PADDLE_WITH_HIP PD_REGISTER_KERNEL(qr, GPU, ALL_LAYOUT, phi::QrKernel, float, double) {} #else PD_REGISTER_KERNEL(qr, GPU, ALL_LAYOUT, phi::QrKernel, float, double, phi::complex64, phi::complex128) {} #endif