// 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. #ifdef PADDLE_WITH_MAGMA #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/common/memory_utils.h" #ifdef PADDLE_WITH_CUDA #include "paddle/phi/backends/dynload/cublas.h" #include "paddle/phi/backends/dynload/cusolver.h" #include "paddle/phi/backends/gpu/cuda/cuda_graph_with_memory_pool.h" #endif // PADDLE_WITH_CUDA #ifdef PADDLE_WITH_HIP #include "hip/hip_runtime.h" #include "paddle/phi/backends/dynload/rocblas.h" #include "paddle/phi/backends/dynload/rocsolver.h" #include "rocblas/rocblas.h" #include "rocsolver/rocsolver.h" #endif // PADDLE_WITH_HIP #endif // PADDLE_WITH_MAGMA #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/complex_kernel.h" #include "paddle/phi/kernels/cpu/eig.h" #include "paddle/phi/kernels/funcs/complex_functors.h" #include "paddle/phi/kernels/funcs/for_range.h" #include "paddle/phi/kernels/funcs/unsqueeze.h" #include "paddle/phi/kernels/transpose_kernel.h" namespace phi { #ifdef PADDLE_WITH_MAGMA template void SolveLinearSystemGPU(const GPUContext& dev_ctx, const T* matrix_data, const T* rhs_data, T* out_data, int order, int rhs_cols, int batch_count); #ifdef PADDLE_WITH_CUDA template <> void SolveLinearSystemGPU>( const GPUContext& dev_ctx, const dtype::complex* matrix_data, // device ptr, row-major, size batch*order*order const dtype::complex* rhs_data, // device ptr, row-major, size batch*order*rhs_cols dtype::complex* out_data, // device ptr, row-major, size batch*order*rhs_cols int order, int rhs_cols, int batch_count) { // handles cublasHandle_t cublas_handle = dev_ctx.cublas_handle(); cusolverDnHandle_t cusolver_handle = dev_ctx.cusolver_dn_handle(); auto stream = Stream(reinterpret_cast(dev_ctx.stream())); // cuComplex constants const cuComplex kAlpha = make_cuFloatComplex(1.0f, 0.0f); const cuComplex kZero = make_cuFloatComplex(0.0f, 0.0f); // Sizes const size_t A_one_bytes = static_cast(order) * order * sizeof(cuComplex); const size_t B_one_bytes = static_cast(order) * rhs_cols * sizeof(cuComplex); const size_t A_batch_bytes = A_one_bytes * batch_count; const size_t B_batch_bytes = B_one_bytes * batch_count; const cuComplex* A_row_all = reinterpret_cast(matrix_data); const cuComplex* B_row_all = reinterpret_cast(rhs_data); cuComplex* X_row_all = reinterpret_cast(out_data); auto dA_col_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), A_batch_bytes, stream); auto dB_col_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), B_batch_bytes, stream); cuComplex* dA_col = reinterpret_cast(dA_col_alloc->ptr()); cuComplex* dB_col = reinterpret_cast(dB_col_alloc->ptr()); auto d_pivots_alloc = memory_utils::Alloc( dev_ctx.GetPlace(), static_cast(batch_count) * order * sizeof(int), stream); int* d_pivots = reinterpret_cast(d_pivots_alloc->ptr()); auto d_info_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), static_cast(batch_count) * sizeof(int), stream); int* d_info = reinterpret_cast(d_info_alloc->ptr()); // A_row layout: row-major (order x order), B_row layout: row-major (order // x rhs_cols) for (int i = 0; i < batch_count; ++i) { const cuComplex* A_row = A_row_all + static_cast(i) * order * order; cuComplex* A_col = dA_col + static_cast(i) * order * order; const cuComplex* B_row = B_row_all + static_cast(i) * order * rhs_cols; cuComplex* B_col = dB_col + static_cast(i) * order * rhs_cols; // transpose A_row (row-major) -> A_col (column-major) via C = A^T PADDLE_ENFORCE_GPU_SUCCESS( dynload::cublasCgeam(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, order, order, &kAlpha, A_row, order, // lda: when interpreting A_row as (order x // order) row-major, using order &kZero, nullptr, order, A_col, order)); // ldc = order (column-major leading dim) // transpose B_row (row-major order x rhs_cols) -> B_col (column-major order // x rhs_cols) PADDLE_ENFORCE_GPU_SUCCESS(dynload::cublasCgeam( cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, order, rhs_cols, &kAlpha, B_row, rhs_cols, // lda when A_row is viewed row-major: leading = rhs_cols &kZero, nullptr, rhs_cols, B_col, order)); // ldc = order } int lwork = 0; cuComplex* dA_col0 = dA_col; PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnCgetrf_bufferSize( cusolver_handle, order, order, dA_col0, order, &lwork)); size_t work_bytes = static_cast(lwork) * sizeof(cuComplex); auto d_work_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), work_bytes, stream); cuComplex* d_work = reinterpret_cast(d_work_alloc->ptr()); for (int i = 0; i < batch_count; ++i) { cuComplex* A_col = dA_col + static_cast(i) * order * order; cuComplex* B_col = dB_col + static_cast(i) * order * rhs_cols; int* pivots_i = d_pivots + static_cast(i) * order; int* info_i = d_info + i; // getrf (LU factorization) on A_col (column-major) PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnCgetrf( cusolver_handle, order, order, A_col, order, d_work, pivots_i, info_i)); // getrs: solve A_col * X_col = B_col PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnCgetrs( cusolver_handle, CUBLAS_OP_N, // no transpose on column-major matrix order, rhs_cols, A_col, order, pivots_i, B_col, order, info_i)); } for (int i = 0; i < batch_count; ++i) { cuComplex* B_col = dB_col + static_cast(i) * order * rhs_cols; // X in column-major cuComplex* X_row = X_row_all + static_cast(i) * order * rhs_cols; // target row-major // transpose X_col -> X_row // We use C = A^T : A has shape (order x rhs_cols) in column-major, so C // will be (rhs_cols x order), but we want X_row with shape (order x // rhs_cols) in row-major; calling cublasCgeam with op=T and adjusted dims // works: PADDLE_ENFORCE_GPU_SUCCESS(dynload::cublasCgeam( cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, rhs_cols, order, // rowsC = rhs_cols, colsC = order &kAlpha, B_col, order, // B_col lda = order (col-major) &kZero, nullptr, order, X_row, rhs_cols)); // X_row ldc = rhs_cols (row-major leading dimension) } std::vector h_info(batch_count, 0); PADDLE_ENFORCE_EQ( backends::gpu::IsCUDAGraphCapturing(), false, common::errors::InvalidArgument( "EigGradKernel does not support CUDA Graph capture: async D2H copy " "to local vector 'h_info' will bake the destination address into the " "graph; on replay the vector is re-created at a different address, " "causing a dangling-pointer write.")); phi::memory_utils::Copy(CPUPlace(), h_info.data(), dev_ctx.GetPlace(), d_info, static_cast(batch_count) * sizeof(int), reinterpret_cast(dev_ctx.stream())); dev_ctx.Wait(); for (int i = 0; i < batch_count; ++i) { PADDLE_ENFORCE_EQ( h_info[i], 0, errors::External( "cuSOLVER getrf/getrs failed at batch %d, info: %d", i, h_info[i])); } } template <> void SolveLinearSystemGPU>( const GPUContext& dev_ctx, const dtype::complex* matrix_data, // device ptr, row-major, size batch*order*order const dtype::complex* rhs_data, // device ptr, row-major, size batch*order*rhs_cols dtype::complex* out_data, // device ptr, row-major, size batch*order*rhs_cols int order, int rhs_cols, int batch_count) { // handles cublasHandle_t cublas_handle = dev_ctx.cublas_handle(); cusolverDnHandle_t cusolver_handle = dev_ctx.cusolver_dn_handle(); auto stream = Stream(reinterpret_cast(dev_ctx.stream())); // cuDoubleComplex constants const cuDoubleComplex kAlpha = make_cuDoubleComplex(1.0f, 0.0f); const cuDoubleComplex kZero = make_cuDoubleComplex(0.0f, 0.0f); // Sizes const size_t A_one_bytes = static_cast(order) * order * sizeof(cuDoubleComplex); const size_t B_one_bytes = static_cast(order) * rhs_cols * sizeof(cuDoubleComplex); const size_t A_batch_bytes = A_one_bytes * batch_count; const size_t B_batch_bytes = B_one_bytes * batch_count; const cuDoubleComplex* A_row_all = reinterpret_cast(matrix_data); const cuDoubleComplex* B_row_all = reinterpret_cast(rhs_data); cuDoubleComplex* X_row_all = reinterpret_cast(out_data); auto dA_col_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), A_batch_bytes, stream); auto dB_col_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), B_batch_bytes, stream); cuDoubleComplex* dA_col = reinterpret_cast(dA_col_alloc->ptr()); cuDoubleComplex* dB_col = reinterpret_cast(dB_col_alloc->ptr()); auto d_pivots_alloc = memory_utils::Alloc( dev_ctx.GetPlace(), static_cast(batch_count) * order * sizeof(int), stream); int* d_pivots = reinterpret_cast(d_pivots_alloc->ptr()); auto d_info_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), static_cast(batch_count) * sizeof(int), stream); int* d_info = reinterpret_cast(d_info_alloc->ptr()); // A_row layout: row-major (order x order), B_row layout: row-major (order // x rhs_cols) for (int i = 0; i < batch_count; ++i) { const cuDoubleComplex* A_row = A_row_all + static_cast(i) * order * order; cuDoubleComplex* A_col = dA_col + static_cast(i) * order * order; const cuDoubleComplex* B_row = B_row_all + static_cast(i) * order * rhs_cols; cuDoubleComplex* B_col = dB_col + static_cast(i) * order * rhs_cols; // transpose A_row (row-major) -> A_col (column-major) via C = A^T PADDLE_ENFORCE_GPU_SUCCESS( dynload::cublasZgeam(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, order, order, &kAlpha, A_row, order, // lda: when interpreting A_row as (order x // order) row-major, using order &kZero, nullptr, order, A_col, order)); // ldc = order (column-major leading dim) // transpose B_row (row-major order x rhs_cols) -> B_col (column-major order // x rhs_cols) PADDLE_ENFORCE_GPU_SUCCESS(dynload::cublasZgeam( cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, order, rhs_cols, &kAlpha, B_row, rhs_cols, // lda when A_row is viewed row-major: leading = rhs_cols &kZero, nullptr, rhs_cols, B_col, order)); // ldc = order } int lwork = 0; cuDoubleComplex* dA_col0 = dA_col; PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnZgetrf_bufferSize( cusolver_handle, order, order, dA_col0, order, &lwork)); size_t work_bytes = static_cast(lwork) * sizeof(cuDoubleComplex); auto d_work_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), work_bytes, stream); cuDoubleComplex* d_work = reinterpret_cast(d_work_alloc->ptr()); for (int i = 0; i < batch_count; ++i) { cuDoubleComplex* A_col = dA_col + static_cast(i) * order * order; cuDoubleComplex* B_col = dB_col + static_cast(i) * order * rhs_cols; int* pivots_i = d_pivots + static_cast(i) * order; int* info_i = d_info + i; // getrf (LU factorization) on A_col (column-major) PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnZgetrf( cusolver_handle, order, order, A_col, order, d_work, pivots_i, info_i)); // getrs: solve A_col * X_col = B_col PADDLE_ENFORCE_GPU_SUCCESS(dynload::cusolverDnZgetrs( cusolver_handle, CUBLAS_OP_N, // no transpose on column-major matrix order, rhs_cols, A_col, order, pivots_i, B_col, order, info_i)); } for (int i = 0; i < batch_count; ++i) { cuDoubleComplex* B_col = dB_col + static_cast(i) * order * rhs_cols; // X in column-major cuDoubleComplex* X_row = X_row_all + static_cast(i) * order * rhs_cols; // target row-major // transpose X_col -> X_row // We use C = A^T : A has shape (order x rhs_cols) in column-major, so C // will be (rhs_cols x order), but we want X_row with shape (order x // rhs_cols) in row-major; calling cublasZgeam with op=T and adjusted dims // works: PADDLE_ENFORCE_GPU_SUCCESS(dynload::cublasZgeam( cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, rhs_cols, order, // rowsC = rhs_cols, colsC = order &kAlpha, B_col, order, // B_col lda = order (col-major) &kZero, nullptr, order, X_row, rhs_cols)); // X_row ldc = rhs_cols (row-major leading dimension) } std::vector h_info(batch_count, 0); PADDLE_ENFORCE_EQ( backends::gpu::IsCUDAGraphCapturing(), false, common::errors::InvalidArgument( "EigGradKernel does not support CUDA Graph capture: async D2H copy " "to local vector 'h_info' will bake the destination address into the " "graph; on replay the vector is re-created at a different address, " "causing a dangling-pointer write.")); phi::memory_utils::Copy(CPUPlace(), h_info.data(), dev_ctx.GetPlace(), d_info, static_cast(batch_count) * sizeof(int), reinterpret_cast(dev_ctx.stream())); dev_ctx.Wait(); for (int i = 0; i < batch_count; ++i) { PADDLE_ENFORCE_EQ( h_info[i], 0, errors::External( "cuSOLVER getrf/getrs failed at batch %d, info: %d", i, h_info[i])); } } #endif // PADDLE_WITH_CUDA #ifdef PADDLE_WITH_HIP template <> void SolveLinearSystemGPU>( const GPUContext& dev_ctx, const dtype::complex* matrix_data, // device ptr, row-major, size batch*order*order const dtype::complex* rhs_data, // device ptr, row-major, size batch*order*rhs_cols dtype::complex* out_data, // device ptr, row-major, size batch*order*rhs_cols int order, int rhs_cols, int batch_count) { // handles rocblas_handle rocblas_handle = dev_ctx.cusolver_dn_handle(); auto stream = Stream(reinterpret_cast(dev_ctx.stream())); // rocblas_float_complex constants const rocblas_float_complex kAlpha = rocblas_float_complex{1.0f, 0.0f}; const rocblas_float_complex kZero = rocblas_float_complex{0.0f, 0.0f}; // Sizes const size_t A_one_bytes = static_cast(order) * order * sizeof(rocblas_float_complex); const size_t B_one_bytes = static_cast(order) * rhs_cols * sizeof(rocblas_float_complex); const size_t A_batch_bytes = A_one_bytes * batch_count; const size_t B_batch_bytes = B_one_bytes * batch_count; const rocblas_float_complex* A_row_all = reinterpret_cast(matrix_data); const rocblas_float_complex* B_row_all = reinterpret_cast(rhs_data); rocblas_float_complex* X_row_all = reinterpret_cast(out_data); auto dA_col_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), A_batch_bytes, stream); auto dB_col_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), B_batch_bytes, stream); rocblas_float_complex* dA_col = reinterpret_cast(dA_col_alloc->ptr()); rocblas_float_complex* dB_col = reinterpret_cast(dB_col_alloc->ptr()); auto d_pivots_alloc = memory_utils::Alloc( dev_ctx.GetPlace(), static_cast(batch_count) * order * sizeof(rocblas_int), stream); rocblas_int* d_pivots = reinterpret_cast(d_pivots_alloc->ptr()); auto d_info_alloc = memory_utils::Alloc( dev_ctx.GetPlace(), static_cast(batch_count) * sizeof(rocblas_int), stream); rocblas_int* d_info = reinterpret_cast(d_info_alloc->ptr()); // A_row layout: row-major (order x order), B_row layout: row-major (order x // rhs_cols) for (int i = 0; i < batch_count; ++i) { const rocblas_float_complex* A_row = A_row_all + static_cast(i) * order * order; rocblas_float_complex* A_col = dA_col + static_cast(i) * order * order; const rocblas_float_complex* B_row = B_row_all + static_cast(i) * order * rhs_cols; rocblas_float_complex* B_col = dB_col + static_cast(i) * order * rhs_cols; // transpose A_row (row-major) -> A_col (column-major) via C = A^T PADDLE_ENFORCE_GPU_SUCCESS(rocblas_cgeam( rocblas_handle, rocblas_operation_transpose, rocblas_operation_none, order, order, &kAlpha, A_row, order, // lda: when interpreting A_row as (order x order) row-major &kZero, nullptr, order, A_col, order)); // ldc = order (column-major leading dim) // transpose B_row (row-major order x rhs_cols) -> B_col (column-major order // x rhs_cols) PADDLE_ENFORCE_GPU_SUCCESS(rocblas_cgeam( rocblas_handle, rocblas_operation_transpose, rocblas_operation_none, order, rhs_cols, &kAlpha, B_row, rhs_cols, // lda when A_row is viewed row-major: leading = rhs_cols &kZero, nullptr, rhs_cols, B_col, order)); // ldc = order } // LU factorization and solve for each batch for (int i = 0; i < batch_count; ++i) { rocblas_float_complex* A_col = dA_col + static_cast(i) * order * order; rocblas_float_complex* B_col = dB_col + static_cast(i) * order * rhs_cols; rocblas_int* pivots_i = d_pivots + static_cast(i) * order; rocblas_int* info_i = d_info + i; // getrf (LU factorization) on A_col (column-major) PADDLE_ENFORCE_GPU_SUCCESS(rocsolver_cgetrf( rocblas_handle, order, order, A_col, order, pivots_i, info_i)); // getrs: solve A_col * X_col = B_col PADDLE_ENFORCE_GPU_SUCCESS(rocsolver_cgetrs( rocblas_handle, rocblas_operation_none, // no transpose on column-major matrix order, rhs_cols, A_col, order, pivots_i, B_col, order)); } // Transpose results back to row-major for (int i = 0; i < batch_count; ++i) { rocblas_float_complex* B_col = dB_col + static_cast(i) * order * rhs_cols; // X in column-major rocblas_float_complex* X_row = X_row_all + static_cast(i) * order * rhs_cols; // target row-major // transpose X_col -> X_row PADDLE_ENFORCE_GPU_SUCCESS(rocblas_cgeam( rocblas_handle, rocblas_operation_transpose, rocblas_operation_none, rhs_cols, order, // rowsC = rhs_cols, colsC = order &kAlpha, B_col, order, // B_col lda = order (col-major) &kZero, nullptr, order, X_row, rhs_cols)); // X_row ldc = rhs_cols (row-major leading dimension) } // Check error info CPUPlace cpu_place; DeviceContextPool& pool = DeviceContextPool::Instance(); auto* cpu_ctx = static_cast(pool.Get(cpu_place)); std::vector h_info(batch_count, 0); memory_utils::Copy(CPUPlace(), h_info.data(), dev_ctx.GetPlace(), d_info, static_cast(batch_count) * sizeof(rocblas_int), reinterpret_cast(dev_ctx.stream())); dev_ctx.Wait(); for (int i = 0; i < batch_count; ++i) { PADDLE_ENFORCE_EQ( h_info[i], 0, errors::External("rocSOLVER getrf/getrs failed at batch %d, info: %d", i, h_info[i])); } } template <> void SolveLinearSystemGPU>( const GPUContext& dev_ctx, const dtype::complex* matrix_data, // device ptr, row-major, size batch*order*order const dtype::complex* rhs_data, // device ptr, row-major, size batch*order*rhs_cols dtype::complex* out_data, // device ptr, row-major, size batch*order*rhs_cols int order, int rhs_cols, int batch_count) { // handles rocblas_handle rocblas_handle = dev_ctx.cusolver_dn_handle(); auto stream = Stream(reinterpret_cast(dev_ctx.stream())); // rocblas_double_complex constants const rocblas_double_complex kAlpha = rocblas_double_complex{1.0, 0.0}; const rocblas_double_complex kZero = rocblas_double_complex{0.0, 0.0}; // Sizes const size_t A_one_bytes = static_cast(order) * order * sizeof(rocblas_double_complex); const size_t B_one_bytes = static_cast(order) * rhs_cols * sizeof(rocblas_double_complex); const size_t A_batch_bytes = A_one_bytes * batch_count; const size_t B_batch_bytes = B_one_bytes * batch_count; const rocblas_double_complex* A_row_all = reinterpret_cast(matrix_data); const rocblas_double_complex* B_row_all = reinterpret_cast(rhs_data); rocblas_double_complex* X_row_all = reinterpret_cast(out_data); auto dA_col_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), A_batch_bytes, stream); auto dB_col_alloc = memory_utils::Alloc(dev_ctx.GetPlace(), B_batch_bytes, stream); rocblas_double_complex* dA_col = reinterpret_cast(dA_col_alloc->ptr()); rocblas_double_complex* dB_col = reinterpret_cast(dB_col_alloc->ptr()); auto d_pivots_alloc = memory_utils::Alloc( dev_ctx.GetPlace(), static_cast(batch_count) * order * sizeof(rocblas_int), stream); rocblas_int* d_pivots = reinterpret_cast(d_pivots_alloc->ptr()); auto d_info_alloc = memory_utils::Alloc( dev_ctx.GetPlace(), static_cast(batch_count) * sizeof(rocblas_int), stream); rocblas_int* d_info = reinterpret_cast(d_info_alloc->ptr()); // A_row layout: row-major (order x order), B_row layout: row-major (order x // rhs_cols) for (int i = 0; i < batch_count; ++i) { const rocblas_double_complex* A_row = A_row_all + static_cast(i) * order * order; rocblas_double_complex* A_col = dA_col + static_cast(i) * order * order; const rocblas_double_complex* B_row = B_row_all + static_cast(i) * order * rhs_cols; rocblas_double_complex* B_col = dB_col + static_cast(i) * order * rhs_cols; // transpose A_row (row-major) -> A_col (column-major) via C = A^T PADDLE_ENFORCE_GPU_SUCCESS(rocblas_zgeam( rocblas_handle, rocblas_operation_transpose, rocblas_operation_none, order, order, &kAlpha, A_row, order, // lda: when interpreting A_row as (order x order) row-major &kZero, nullptr, order, A_col, order)); // ldc = order (column-major leading dim) // transpose B_row (row-major order x rhs_cols) -> B_col (column-major order // x rhs_cols) PADDLE_ENFORCE_GPU_SUCCESS(rocblas_zgeam( rocblas_handle, rocblas_operation_transpose, rocblas_operation_none, order, rhs_cols, &kAlpha, B_row, rhs_cols, // lda when A_row is viewed row-major: leading = rhs_cols &kZero, nullptr, rhs_cols, B_col, order)); // ldc = order } // LU factorization and solve for each batch for (int i = 0; i < batch_count; ++i) { rocblas_double_complex* A_col = dA_col + static_cast(i) * order * order; rocblas_double_complex* B_col = dB_col + static_cast(i) * order * rhs_cols; rocblas_int* pivots_i = d_pivots + static_cast(i) * order; rocblas_int* info_i = d_info + i; // getrf (LU factorization) on A_col (column-major) PADDLE_ENFORCE_GPU_SUCCESS(rocsolver_zgetrf( rocblas_handle, order, order, A_col, order, pivots_i, info_i)); // getrs: solve A_col * X_col = B_col PADDLE_ENFORCE_GPU_SUCCESS(rocsolver_zgetrs( rocblas_handle, rocblas_operation_none, // no transpose on column-major matrix order, rhs_cols, A_col, order, pivots_i, B_col, order)); } // Transpose results back to row-major for (int i = 0; i < batch_count; ++i) { rocblas_double_complex* B_col = dB_col + static_cast(i) * order * rhs_cols; // X in column-major rocblas_double_complex* X_row = X_row_all + static_cast(i) * order * rhs_cols; // target row-major // transpose X_col -> X_row PADDLE_ENFORCE_GPU_SUCCESS(rocblas_zgeam( rocblas_handle, rocblas_operation_transpose, rocblas_operation_none, rhs_cols, order, // rowsC = rhs_cols, colsC = order &kAlpha, B_col, order, // B_col lda = order (col-major) &kZero, nullptr, order, X_row, rhs_cols)); // X_row ldc = rhs_cols (row-major leading dimension) } CPUPlace cpu_place; DeviceContextPool& pool = DeviceContextPool::Instance(); auto* cpu_ctx = static_cast(pool.Get(cpu_place)); std::vector h_info(batch_count, 0); memory_utils::Copy(CPUPlace(), h_info.data(), dev_ctx.GetPlace(), d_info, static_cast(batch_count) * sizeof(rocblas_int), reinterpret_cast(dev_ctx.stream())); dev_ctx.Wait(); for (int i = 0; i < batch_count; ++i) { PADDLE_ENFORCE_EQ( h_info[i], 0, errors::External("rocSOLVER getrf/getrs failed at batch %d, info: %d", i, h_info[i])); } } #endif // PADDLE_WITH_HIP template void ComputeBackwardForComplexInputGPU(const DenseTensor& L, const DenseTensor& V, const optional& gL, const optional& gV, T* x_grad_data, int batch_count, int order, const Context& dev_ctx) { DenseTensor gL_safe; if (gL.get_ptr()) { gL_safe = gL.get(); } else { gL_safe = Fill(dev_ctx, vectorize(L.dims()), T(0)); } DenseTensor gV_safe; if (gV.get_ptr()) { gV_safe = gV.get(); } else { gV_safe = Fill(dev_ctx, vectorize(V.dims()), T(0)); } DenseTensor trans_v = TransposeLast2Dim(dev_ctx, V); DenseTensor Vh = Conj(dev_ctx, trans_v); DenseTensor Lconj = Conj(dev_ctx, L); DenseTensor Econj = Subtract( dev_ctx, funcs::Unsqueeze(Lconj, -2), funcs::Unsqueeze(Lconj, -1)); DenseTensor VhgV = Matmul(dev_ctx, Vh, gV_safe); DenseTensor diag_real = Real(dev_ctx, VhgV); auto cpu_place = CPUPlace(); DeviceContextPool& pool = DeviceContextPool::Instance(); auto* cpu_ctx = static_cast(pool.Get(cpu_place)); DenseTensor diag_real_cpu; diag_real_cpu.Resize(diag_real.dims()); Copy(dev_ctx, diag_real, cpu_place, false, &diag_real_cpu); DenseTensor diag_res_cpu = funcs::BatchDiag((*cpu_ctx), diag_real_cpu, batch_count); DenseTensor diag_res; dev_ctx.template Alloc(&diag_res); Copy(dev_ctx, diag_res_cpu, GPUPlace(), false, &diag_res); DenseTensor diag_unsqueezed = funcs::Unsqueeze(diag_res, -2); auto numel = diag_unsqueezed.numel(); DenseTensor diag_unsqueezed_complex; auto* data_diag_un = diag_unsqueezed.data>(); diag_unsqueezed_complex.Resize(diag_unsqueezed.dims()); auto* data_diag_un_com = dev_ctx.template Alloc( &diag_unsqueezed_complex, static_cast(numel * sizeof(T))); funcs::ForRange for_range(dev_ctx, numel); funcs::RealToComplexFunctor functor(data_diag_un, data_diag_un_com, numel); for_range(functor); // real tensor multiply complex tensor in broadcast manner DenseTensor res1 = Multiply(dev_ctx, V, diag_unsqueezed_complex); DenseTensor res2 = Matmul(dev_ctx, Vh, res1); DenseTensor result = Subtract(dev_ctx, VhgV, res2); result.Resize(V.dims()); dev_ctx.template Alloc(&result); result = Divide(dev_ctx, result, Econj); result = funcs::DiagFill(dev_ctx, order, order, order, 0, gL_safe, result); DenseTensor rhs = Matmul(dev_ctx, result, Vh); // solve linear system // solve(Vh, rhs, out, m, k) // Vh: matrix with shape [m,m] // rhs: rhs with shape [m,k] // x_grad: out int64_t m = Vh.dims(-1); int64_t k = rhs.dims(-1); auto* matrix_data = Vh.data(); auto* rhs_data = rhs.data(); SolveLinearSystemGPU( dev_ctx, matrix_data, rhs_data, x_grad_data, m, k, batch_count); } #endif // PADDLE_WITH_MAGMA #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) template void EigGradKernel(const Context& dev_ctx, const DenseTensor& out_w, const DenseTensor& out_v, const optional& dout_w, const optional& dout_v, DenseTensor* dx) { auto* dx_data = dev_ctx.template Alloc>(dx); if (dx->numel() == 0) { return; } auto& dims = out_v.dims(); int batch_count = BatchCount(out_v); const int64_t order = out_v.dims(-1); ComputeBackwardForComplexInputGPU, Context>( out_w, out_v, dout_w, dout_v, dx_data, batch_count, order, dev_ctx); } #endif // PADDLE_WITH_CUDA || PADDLE_WITH_HIP } // namespace phi // Register the kernel #ifdef PADDLE_WITH_MAGMA PD_REGISTER_KERNEL(eig_grad, GPU, ALL_LAYOUT, phi::EigGradKernel, float, double, phi::complex64, phi::complex128) { kernel->InputAt(0).SetDataType(phi::dtype::ToReal(kernel_key.dtype())); kernel->InputAt(2).SetDataType(phi::dtype::ToReal(kernel_key.dtype())); kernel->OutputAt(0).SetDataType(phi::dtype::ToComplex(kernel_key.dtype())); } #endif // PADDLE_WITH_MAGMA