Files
paddlepaddle--paddle/paddle/phi/kernels/cpu/eigvals_kernel.cc
T
2026-07-13 12:40:42 +08:00

268 lines
9.0 KiB
C++

// 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 "paddle/phi/kernels/eigvals_kernel.h"
#include "glog/logging.h"
#include "paddle/phi/backends/cpu/cpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/utils/data_type.h"
#include "paddle/phi/kernels/funcs/complex_functors.h"
#include "paddle/phi/kernels/funcs/for_range.h"
#include "paddle/phi/kernels/funcs/lapack/lapack_function.h"
namespace phi {
template <typename T, typename enable = void>
struct PaddleComplex;
template <typename T>
struct PaddleComplex<
T,
typename std::enable_if<std::is_floating_point<T>::value>::type> {
using type = dtype::complex<T>;
};
template <typename T>
struct PaddleComplex<
T,
typename std::enable_if<
std::is_same<T, dtype::complex<float>>::value ||
std::is_same<T, dtype::complex<double>>::value>::type> {
using type = T;
};
template <typename T>
using PaddleCType = typename PaddleComplex<T>::type;
template <typename T>
using Real = typename dtype::Real<T>;
inline void CheckLapackEigResult(const int info, const std::string& name) {
PADDLE_ENFORCE_LE(
info,
0,
errors::PreconditionNotMet("The QR algorithm failed to compute all the "
"eigenvalues in function %s.",
name.c_str()));
PADDLE_ENFORCE_GE(
info,
0,
errors::InvalidArgument(
"The %d-th argument has an illegal value in function %s.",
-info,
name.c_str()));
}
template <typename T, typename Context>
typename std::enable_if<std::is_floating_point<T>::value>::type LapackEigvals(
const Context& dev_ctx,
const DenseTensor& input,
DenseTensor* output,
DenseTensor* work,
DenseTensor* rwork /*unused*/) {
DenseTensor a; // will be overwritten when lapackEig exit
Copy(dev_ctx, input, input.place(), /*blocking=*/true, &a);
DenseTensor w;
int64_t n_dim = input.dims()[1];
w.Resize({n_dim << 1});
T* w_data = dev_ctx.template Alloc<T>(&w);
int64_t work_mem = static_cast<int64_t>(work->memory_size());
int64_t required_work_mem = 3 * n_dim * sizeof(T);
PADDLE_ENFORCE_GE(
work_mem,
3 * n_dim * sizeof(T),
errors::InvalidArgument(
"The memory size of the work tensor in LapackEigvals function "
"should be at least %" PRId64 " bytes, "
"but received work\'s memory size = %" PRId64 " bytes.",
required_work_mem,
work_mem));
int info = 0;
funcs::lapackEig<T>('N',
'N',
static_cast<int>(n_dim),
a.template data<T>(),
static_cast<int>(n_dim),
w_data,
nullptr,
1,
nullptr,
1,
work->template data<T>(),
static_cast<int>(work_mem / sizeof(T)),
static_cast<T*>(nullptr),
&info);
std::string name = "phi::backend::dynload::dgeev_";
if (input.dtype() == DataType::FLOAT64) {
name = "phi::backend::dynload::sgeev_";
}
CheckLapackEigResult(info, name);
funcs::ForRange<Context> for_range(dev_ctx, n_dim);
funcs::RealImagToComplexFunctor<PaddleCType<T>> functor(
w_data, w_data + n_dim, output->template data<PaddleCType<T>>(), n_dim);
for_range(functor);
}
template <typename T, typename Context>
typename std::enable_if<std::is_same<T, dtype::complex<float>>::value ||
std::is_same<T, dtype::complex<double>>::value>::type
LapackEigvals(const Context& dev_ctx,
const DenseTensor& input,
DenseTensor* output,
DenseTensor* work,
DenseTensor* rwork) {
DenseTensor a; // will be overwritten when lapackEig exit
Copy(dev_ctx, input, input.place(), /*blocking=*/true, &a);
int64_t work_mem = static_cast<int64_t>(work->memory_size());
int64_t n_dim = input.dims()[1];
int64_t required_work_mem = 3 * n_dim * sizeof(T);
PADDLE_ENFORCE_GE(
work_mem,
3 * n_dim * sizeof(T),
errors::InvalidArgument(
"The memory size of the work tensor in LapackEigvals function "
"should be at least %" PRId64 " bytes, "
"but received work\'s memory size = %" PRId64 " bytes.",
required_work_mem,
work_mem));
int64_t rwork_mem = static_cast<int64_t>(rwork->memory_size());
int64_t required_rwork_mem = (n_dim << 1) * sizeof(dtype::Real<T>);
PADDLE_ENFORCE_GE(
rwork_mem,
required_rwork_mem,
errors::InvalidArgument(
"The memory size of the rwork tensor in LapackEigvals function "
"should be at least %" PRId64 " bytes, "
"but received rwork\'s memory size = %" PRId64 " bytes.",
required_rwork_mem,
rwork_mem));
int info = 0;
funcs::lapackEig<T, dtype::Real<T>>('N',
'N',
static_cast<int>(n_dim),
a.template data<T>(),
static_cast<int>(n_dim),
output->template data<T>(),
nullptr,
1,
nullptr,
1,
work->template data<T>(),
static_cast<int>(work_mem / sizeof(T)),
rwork->template data<dtype::Real<T>>(),
&info);
std::string name = "phi::backend::dynload::cgeev_";
if (input.dtype() == DataType::COMPLEX128) {
name = "phi::backend::dynload::zgeev_";
}
CheckLapackEigResult(info, name);
}
void SpiltBatchSquareMatrix(const DenseTensor& input,
std::vector<DenseTensor>* output) {
DDim input_dims = input.dims();
int last_dim = input_dims.size() - 1;
int n_dim = static_cast<int>(input_dims[last_dim]);
DDim flattened_input_dims, flattened_output_dims;
if (input_dims.size() > 2) {
flattened_input_dims =
common::flatten_to_3d(input_dims, last_dim - 1, last_dim);
} else {
flattened_input_dims = make_ddim({1, n_dim, n_dim});
}
DenseTensor flattened_input;
flattened_input.ShareDataWith(input);
flattened_input.Resize(flattened_input_dims);
(*output) = flattened_input.Split(1, 0);
}
template <typename T, typename Context>
void EigvalsKernel(const Context& dev_ctx,
const DenseTensor& x,
DenseTensor* out) {
dev_ctx.template Alloc<PaddleCType<T>>(out);
if (out && out->numel() == 0) {
return;
}
std::vector<DenseTensor> x_matrices;
SpiltBatchSquareMatrix(x, /*->*/ &x_matrices);
int64_t n_dim = x_matrices[0].dims()[1];
int64_t n_batch = static_cast<int64_t>(x_matrices.size());
DDim out_dims = out->dims();
out->Resize({n_batch, n_dim});
std::vector<DenseTensor> out_vectors = out->Split(1, 0);
// query workspace size
T qwork = T();
int info = 0;
funcs::lapackEig<T, dtype::Real<T>>('N',
'N',
static_cast<int>(n_dim),
x_matrices[0].template data<T>(),
static_cast<int>(n_dim),
nullptr,
nullptr,
1,
nullptr,
1,
&qwork,
-1,
static_cast<dtype::Real<T>*>(nullptr),
&info);
int64_t lwork = static_cast<int64_t>(qwork);
DenseTensor work, rwork;
work.Resize({lwork});
dev_ctx.template Alloc<T>(&work);
if (IsComplexType(x.dtype())) {
rwork.Resize({n_dim << 1});
dev_ctx.template Alloc<dtype::Real<T>>(&rwork);
}
for (int64_t i = 0; i < n_batch; ++i) {
LapackEigvals<T, Context>(
dev_ctx, x_matrices[i], &out_vectors[i], &work, &rwork);
}
out->Resize(out_dims);
}
} // namespace phi
PD_REGISTER_KERNEL(eigvals,
CPU,
ALL_LAYOUT,
phi::EigvalsKernel,
float,
double,
phi::complex64,
phi::complex128) {
kernel->OutputAt(0).SetDataType(phi::dtype::ToComplex(kernel_key.dtype()));
}