Files
2026-07-13 12:40:42 +08:00

906 lines
32 KiB
Plaintext

// 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 <typename T>
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<dtype::complex<float>>(
const GPUContext& dev_ctx,
const dtype::complex<float>*
matrix_data, // device ptr, row-major, size batch*order*order
const dtype::complex<float>*
rhs_data, // device ptr, row-major, size batch*order*rhs_cols
dtype::complex<float>*
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<StreamId>(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<size_t>(order) * order * sizeof(cuComplex);
const size_t B_one_bytes =
static_cast<size_t>(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<const cuComplex*>(matrix_data);
const cuComplex* B_row_all = reinterpret_cast<const cuComplex*>(rhs_data);
cuComplex* X_row_all = reinterpret_cast<cuComplex*>(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<cuComplex*>(dA_col_alloc->ptr());
cuComplex* dB_col = reinterpret_cast<cuComplex*>(dB_col_alloc->ptr());
auto d_pivots_alloc = memory_utils::Alloc(
dev_ctx.GetPlace(),
static_cast<size_t>(batch_count) * order * sizeof(int),
stream);
int* d_pivots = reinterpret_cast<int*>(d_pivots_alloc->ptr());
auto d_info_alloc =
memory_utils::Alloc(dev_ctx.GetPlace(),
static_cast<size_t>(batch_count) * sizeof(int),
stream);
int* d_info = reinterpret_cast<int*>(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<size_t>(i) * order * order;
cuComplex* A_col = dA_col + static_cast<size_t>(i) * order * order;
const cuComplex* B_row =
B_row_all + static_cast<size_t>(i) * order * rhs_cols;
cuComplex* B_col = dB_col + static_cast<size_t>(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<size_t>(lwork) * sizeof(cuComplex);
auto d_work_alloc =
memory_utils::Alloc(dev_ctx.GetPlace(), work_bytes, stream);
cuComplex* d_work = reinterpret_cast<cuComplex*>(d_work_alloc->ptr());
for (int i = 0; i < batch_count; ++i) {
cuComplex* A_col = dA_col + static_cast<size_t>(i) * order * order;
cuComplex* B_col = dB_col + static_cast<size_t>(i) * order * rhs_cols;
int* pivots_i = d_pivots + static_cast<size_t>(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<size_t>(i) * order *
rhs_cols; // X in column-major
cuComplex* X_row = X_row_all + static_cast<size_t>(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<int> 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<size_t>(batch_count) * sizeof(int),
reinterpret_cast<void*>(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<dtype::complex<double>>(
const GPUContext& dev_ctx,
const dtype::complex<double>*
matrix_data, // device ptr, row-major, size batch*order*order
const dtype::complex<double>*
rhs_data, // device ptr, row-major, size batch*order*rhs_cols
dtype::complex<double>*
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<StreamId>(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<size_t>(order) * order * sizeof(cuDoubleComplex);
const size_t B_one_bytes =
static_cast<size_t>(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<const cuDoubleComplex*>(matrix_data);
const cuDoubleComplex* B_row_all =
reinterpret_cast<const cuDoubleComplex*>(rhs_data);
cuDoubleComplex* X_row_all = reinterpret_cast<cuDoubleComplex*>(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<cuDoubleComplex*>(dA_col_alloc->ptr());
cuDoubleComplex* dB_col =
reinterpret_cast<cuDoubleComplex*>(dB_col_alloc->ptr());
auto d_pivots_alloc = memory_utils::Alloc(
dev_ctx.GetPlace(),
static_cast<size_t>(batch_count) * order * sizeof(int),
stream);
int* d_pivots = reinterpret_cast<int*>(d_pivots_alloc->ptr());
auto d_info_alloc =
memory_utils::Alloc(dev_ctx.GetPlace(),
static_cast<size_t>(batch_count) * sizeof(int),
stream);
int* d_info = reinterpret_cast<int*>(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<size_t>(i) * order * order;
cuDoubleComplex* A_col = dA_col + static_cast<size_t>(i) * order * order;
const cuDoubleComplex* B_row =
B_row_all + static_cast<size_t>(i) * order * rhs_cols;
cuDoubleComplex* B_col = dB_col + static_cast<size_t>(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<size_t>(lwork) * sizeof(cuDoubleComplex);
auto d_work_alloc =
memory_utils::Alloc(dev_ctx.GetPlace(), work_bytes, stream);
cuDoubleComplex* d_work =
reinterpret_cast<cuDoubleComplex*>(d_work_alloc->ptr());
for (int i = 0; i < batch_count; ++i) {
cuDoubleComplex* A_col = dA_col + static_cast<size_t>(i) * order * order;
cuDoubleComplex* B_col = dB_col + static_cast<size_t>(i) * order * rhs_cols;
int* pivots_i = d_pivots + static_cast<size_t>(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<size_t>(i) * order *
rhs_cols; // X in column-major
cuDoubleComplex* X_row = X_row_all + static_cast<size_t>(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<int> 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<size_t>(batch_count) * sizeof(int),
reinterpret_cast<void*>(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<dtype::complex<float>>(
const GPUContext& dev_ctx,
const dtype::complex<float>*
matrix_data, // device ptr, row-major, size batch*order*order
const dtype::complex<float>*
rhs_data, // device ptr, row-major, size batch*order*rhs_cols
dtype::complex<float>*
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<StreamId>(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<size_t>(order) * order * sizeof(rocblas_float_complex);
const size_t B_one_bytes =
static_cast<size_t>(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<const rocblas_float_complex*>(matrix_data);
const rocblas_float_complex* B_row_all =
reinterpret_cast<const rocblas_float_complex*>(rhs_data);
rocblas_float_complex* X_row_all =
reinterpret_cast<rocblas_float_complex*>(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<rocblas_float_complex*>(dA_col_alloc->ptr());
rocblas_float_complex* dB_col =
reinterpret_cast<rocblas_float_complex*>(dB_col_alloc->ptr());
auto d_pivots_alloc = memory_utils::Alloc(
dev_ctx.GetPlace(),
static_cast<size_t>(batch_count) * order * sizeof(rocblas_int),
stream);
rocblas_int* d_pivots = reinterpret_cast<rocblas_int*>(d_pivots_alloc->ptr());
auto d_info_alloc = memory_utils::Alloc(
dev_ctx.GetPlace(),
static_cast<size_t>(batch_count) * sizeof(rocblas_int),
stream);
rocblas_int* d_info = reinterpret_cast<rocblas_int*>(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<size_t>(i) * order * order;
rocblas_float_complex* A_col =
dA_col + static_cast<size_t>(i) * order * order;
const rocblas_float_complex* B_row =
B_row_all + static_cast<size_t>(i) * order * rhs_cols;
rocblas_float_complex* B_col =
dB_col + static_cast<size_t>(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<size_t>(i) * order * order;
rocblas_float_complex* B_col =
dB_col + static_cast<size_t>(i) * order * rhs_cols;
rocblas_int* pivots_i = d_pivots + static_cast<size_t>(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<size_t>(i) * order *
rhs_cols; // X in column-major
rocblas_float_complex* X_row =
X_row_all +
static_cast<size_t>(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<CPUContext*>(pool.Get(cpu_place));
std::vector<rocblas_int> h_info(batch_count, 0);
memory_utils::Copy(CPUPlace(),
h_info.data(),
dev_ctx.GetPlace(),
d_info,
static_cast<size_t>(batch_count) * sizeof(rocblas_int),
reinterpret_cast<void*>(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<dtype::complex<double>>(
const GPUContext& dev_ctx,
const dtype::complex<double>*
matrix_data, // device ptr, row-major, size batch*order*order
const dtype::complex<double>*
rhs_data, // device ptr, row-major, size batch*order*rhs_cols
dtype::complex<double>*
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<StreamId>(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<size_t>(order) * order * sizeof(rocblas_double_complex);
const size_t B_one_bytes =
static_cast<size_t>(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<const rocblas_double_complex*>(matrix_data);
const rocblas_double_complex* B_row_all =
reinterpret_cast<const rocblas_double_complex*>(rhs_data);
rocblas_double_complex* X_row_all =
reinterpret_cast<rocblas_double_complex*>(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<rocblas_double_complex*>(dA_col_alloc->ptr());
rocblas_double_complex* dB_col =
reinterpret_cast<rocblas_double_complex*>(dB_col_alloc->ptr());
auto d_pivots_alloc = memory_utils::Alloc(
dev_ctx.GetPlace(),
static_cast<size_t>(batch_count) * order * sizeof(rocblas_int),
stream);
rocblas_int* d_pivots = reinterpret_cast<rocblas_int*>(d_pivots_alloc->ptr());
auto d_info_alloc = memory_utils::Alloc(
dev_ctx.GetPlace(),
static_cast<size_t>(batch_count) * sizeof(rocblas_int),
stream);
rocblas_int* d_info = reinterpret_cast<rocblas_int*>(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<size_t>(i) * order * order;
rocblas_double_complex* A_col =
dA_col + static_cast<size_t>(i) * order * order;
const rocblas_double_complex* B_row =
B_row_all + static_cast<size_t>(i) * order * rhs_cols;
rocblas_double_complex* B_col =
dB_col + static_cast<size_t>(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<size_t>(i) * order * order;
rocblas_double_complex* B_col =
dB_col + static_cast<size_t>(i) * order * rhs_cols;
rocblas_int* pivots_i = d_pivots + static_cast<size_t>(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<size_t>(i) * order *
rhs_cols; // X in column-major
rocblas_double_complex* X_row =
X_row_all +
static_cast<size_t>(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<CPUContext*>(pool.Get(cpu_place));
std::vector<rocblas_int> h_info(batch_count, 0);
memory_utils::Copy(CPUPlace(),
h_info.data(),
dev_ctx.GetPlace(),
d_info,
static_cast<size_t>(batch_count) * sizeof(rocblas_int),
reinterpret_cast<void*>(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 <typename T, typename Context>
void ComputeBackwardForComplexInputGPU(const DenseTensor& L,
const DenseTensor& V,
const optional<DenseTensor>& gL,
const optional<DenseTensor>& 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<T, Context>(dev_ctx, vectorize<int64_t>(L.dims()), T(0));
}
DenseTensor gV_safe;
if (gV.get_ptr()) {
gV_safe = gV.get();
} else {
gV_safe = Fill<T, Context>(dev_ctx, vectorize<int64_t>(V.dims()), T(0));
}
DenseTensor trans_v = TransposeLast2Dim<T>(dev_ctx, V);
DenseTensor Vh = Conj<T>(dev_ctx, trans_v);
DenseTensor Lconj = Conj<T>(dev_ctx, L);
DenseTensor Econj = Subtract<T>(
dev_ctx, funcs::Unsqueeze(Lconj, -2), funcs::Unsqueeze(Lconj, -1));
DenseTensor VhgV = Matmul<T>(dev_ctx, Vh, gV_safe);
DenseTensor diag_real = Real<T>(dev_ctx, VhgV);
auto cpu_place = CPUPlace();
DeviceContextPool& pool = DeviceContextPool::Instance();
auto* cpu_ctx = static_cast<CPUContext*>(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<T>((*cpu_ctx), diag_real_cpu, batch_count);
DenseTensor diag_res;
dev_ctx.template Alloc<T>(&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<dtype::Real<T>>();
diag_unsqueezed_complex.Resize(diag_unsqueezed.dims());
auto* data_diag_un_com = dev_ctx.template Alloc<T>(
&diag_unsqueezed_complex, static_cast<size_t>(numel * sizeof(T)));
funcs::ForRange<Context> for_range(dev_ctx, numel);
funcs::RealToComplexFunctor<T> functor(data_diag_un, data_diag_un_com, numel);
for_range(functor);
// real tensor multiply complex tensor in broadcast manner
DenseTensor res1 = Multiply<T>(dev_ctx, V, diag_unsqueezed_complex);
DenseTensor res2 = Matmul<T>(dev_ctx, Vh, res1);
DenseTensor result = Subtract<T>(dev_ctx, VhgV, res2);
result.Resize(V.dims());
dev_ctx.template Alloc<T>(&result);
result = Divide<T>(dev_ctx, result, Econj);
result =
funcs::DiagFill<T, T>(dev_ctx, order, order, order, 0, gL_safe, result);
DenseTensor rhs = Matmul<T>(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<T>();
auto* rhs_data = rhs.data<T>();
SolveLinearSystemGPU<T>(
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 <typename T, typename Context>
void EigGradKernel(const Context& dev_ctx,
const DenseTensor& out_w,
const DenseTensor& out_v,
const optional<DenseTensor>& dout_w,
const optional<DenseTensor>& dout_v,
DenseTensor* dx) {
auto* dx_data = dev_ctx.template Alloc<dtype::Complex<T>>(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<dtype::Complex<T>, 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