cuBLAS与Eigen库特征向量计算结果不符的解决方案咨询
我需要对如下4x4协方差矩阵执行特征值分解(EVD):
cuDoubleComplex m_cov_[16] = { make_cuDoubleComplex(2.0301223848037391, 3.4235008150792548e-17), make_cuDoubleComplex(1.0365842528472908, -2.1028119220635375), make_cuDoubleComplex(2.7516978261134084, 0.95059712173944222), make_cuDoubleComplex(-1.350157109070875, -1.1815219722269694), make_cuDoubleComplex(1.0365842528472908, 2.1028119220635375), make_cuDoubleComplex(2.7073859851827988, 8.7392491301242207e-17), make_cuDoubleComplex(0.42038828834095354, 3.3356003818061963), make_cuDoubleComplex(0.53443422887585779, -2.0017874620940646), make_cuDoubleComplex(2.7516978261134084, -0.95059712173944222), make_cuDoubleComplex(0.42038828834095354, -3.3356003818061963), make_cuDoubleComplex(4.1748595442022722, 2.0100622219496206e-17), make_cuDoubleComplex(-2.3832926547827333, -0.9692696339061293), make_cuDoubleComplex(-1.350157109070875, 1.1815219722269694), make_cuDoubleComplex(0.53443422887585779, 2.0017874620940646), make_cuDoubleComplex(-2.3832926547827333, 0.9692696339061293), make_cuDoubleComplex(1.5855784922744534, -1.5877902777669332e-17) }; // Eigen中的EVD实现 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXcd> eigensolver(m_cov_); eigenValues_ = eigensolver.eigenvalues(); eigenVectors_ = eigensolver.eigenvectors();
Eigen计算得到的特征值:
{{x = -2.2573766836348074e-15, y = 0}, {x = -9.709294041296323e-16, y = 0}, {x = 8.9445808618868127e-17, y = 0}, {x = 10.280850897111305, y = 0}}
对应的特征向量:
{{x = -0.3470929425161523, y = 0}, {x = -0.77713832029830621, y = -0}, {x = -0.15994536685820598, y = 0}, {x = 0.5, y = 0}}, {{x = -0.4597724303808105, y = 0.48181665117044714}, {x = 0.12469176895072034, y = -0.019363390950754053}, {x = -0.28597710463196591, y = 0.45689839613393701}, {x = -0.21684345356939669, y = 0.45053181535169839}}, {{x = -0.42256553981019185, y = -0.42733105533794741}, {x = 0.41437862159459787, y = 0.29068296724286291}, {x = 0.33214873805084277, y = 0.14932354148008731}, {x = 0.45697128218787925, y = 0.2029217761985285}}, {{x = -0.21707002501160269, y = 0.16642011333440535}, {x = -0.27240794554375036, y = 0.22298146715732753}, {x = 0.60350719516570406, y = -0.43247796708208042}, {x = -0.38102789443353835, y = 0.32375568514474662}}}
但使用cuSOLVER调用EVD函数:
CHECK_CUSOLVER(cusolverDnZheevd(cusolverH, CUSOLVER_EIG_MODE_VECTOR, CUBLAS_FILL_MODE_UPPER, N, m_eigen_vec.data(), N, m_eigen_value.data(), d_work, lwork, devInfo));
得到的特征值和特征向量:
// 特征值 {-5.2180864461495171e-16, -3.0110342183593702e-16, 2.8548936444323134e-16, 10.497946406463264} // 特征向量 {{x = -0.38208898741712083, y = 0.41581487741664563}, {x = 0.32043709404849968, y = -0.4517568232420171}, {x = 0.21717632600175682, y = -0.36577789346231687}, {x = -0.33093161501032731, y = 0.28959813033042575}, {x = -0.17547383350755327, y = -0.69642001620440552}, {x = -0.10241833368805221, y = -0.43952076894648784}, {x = 0.047402059701005216, y = 0.14281594546174881}, {x = 0.13099303872894871, y = 0.49065012752041015}, {x = -0.32475905997549959, y = 0.077154117638887132}, {x = -0.32253254381877083, y = 0.2157036870035538}, {x = -0.50261510442239055, y = 0.29617238148252628}, {x = -0.58415934115419899, y = 0.23757380765099248}, {x = -0.23214840790484073, y = 0}, {x = 0.58224779741288002, y = 0}, {x = -0.67532037122395228, y = 0}, {x = 0.38863480971863701, y = 0}}
两者特征值近似一致(三个接近0的值和一个大值),但特征向量完全不同。已通过单元测试确认Eigen结果正确,如何让cuSOLVER得到相同的EVD结果?
1. 对齐矩阵存储顺序与填充模式
Eigen默认使用列主序存储矩阵,cuSOLVER的cusolverDnZheevd同样默认列主序输入,但需确保你传递给cuSOLVER的矩阵确实是列主序格式。同时你指定了CUBLAS_FILL_MODE_UPPER,要确认输入矩阵的上三角部分正确填充,且cuSOLVER仅读取上三角数据(自伴随矩阵下三角是上三角的共轭转置)。若矩阵是行主序传递,需先转换为列主序再传入。
2. 统一特征值排序规则
Eigen的SelfAdjointEigenSolver默认返回升序排列的特征值,cuSOLVER的cusolverDnZheevd默认也是升序,但需确认未修改相关参数。若cuSOLVER特征值顺序与Eigen不同,需对其结果按升序重新排序,同步调整对应特征向量的顺序。
3. 处理退化特征空间的基向量差异
你的矩阵存在三个近似0的特征值,属于退化特征空间。对于这类空间,不同实现可选择不同正交基向量,只要向量张成同一子空间且满足正交性,结果都是正确的。若需和Eigen基完全一致,可对cuSOLVER得到的退化子空间特征向量执行正交化后投影匹配,找到与Eigen基对应的线性组合。
4. 验证矩阵自伴随性传递
确保传递给cuSOLVER的矩阵严格满足自伴随性($A = A^H$)。数值微小误差可能导致计算偏离,可先在CPU上验证输入矩阵的自伴随性,再传递到GPU。
5. 检查cuSOLVER计算状态与参数
查看devInfo返回值,确认计算无错误(devInfo=0表示成功)。若存在精度问题,可尝试调整cuSOLVER的工作精度参数,或使用重载版本指定计算类型。
内容的提问来源于stack exchange,提问作者Weimin Chan

