Eigen - 检查矩阵是否为正(半)定
Eigen - Check if matrix is Positive (Semi-)Definite
我正在实施谱聚类算法,我必须确保矩阵(拉普拉斯矩阵)是半正定矩阵。
检查矩阵是否为正定 (PD) 就足够了,因为可以在特征值中看到 "semi-" 部分。矩阵非常大(nxn,其中 n 是几千的数量级)所以特征分析很昂贵。
Eigen 中是否有任何检查在运行时给出 bool 结果?
如果矩阵不是 PD,Matlab 可以通过抛出异常来使用 chol()
方法给出结果。按照这个想法,Eigen returns 没有抱怨 LLL.llt().matrixL()
的结果,尽管我期待一些 warning/error。
Eigen 也有方法 isPositive
,但是由于 bug 它不能用于具有旧 Eigen 版本的系统。
您可以使用 Cholesky 分解 (LLT),其中 returns Eigen::NumericalIssue
如果矩阵为负,请参阅 documentation。
示例如下:
#include <Eigen/Dense>
#include <iostream>
#include <stdexcept>
int main()
{
Eigen::MatrixXd A(2, 2);
A << 1, 0 , 0, -1; // non semi-positive definitie matrix
std::cout << "The matrix A is" << std::endl << A << std::endl;
Eigen::LLT<Eigen::MatrixXd> lltOfA(A); // compute the Cholesky decomposition of A
if(lltOfA.info() == Eigen::NumericalIssue)
{
throw std::runtime_error("Possibly non semi-positive definitie matrix!");
}
}
除了@vsoftco 的回答外,我们还应检查矩阵对称性,因为 PD/PSD 的定义需要对称矩阵。
Eigen::LLT<Eigen::MatrixXd> A_llt(A);
if (!A.isApprox(A.transpose()) || A_llt.info() == Eigen::NumericalIssue) {
throw std::runtime_error("Possibly non semi-positive definitie matrix!");
}
这项检查很重要,例如一些 Eigen 求解器(如 LTDT) requires PSD(or NSD) matrix input. In fact, there exists non-symmetric and hence non-PSD matrix A
that passes the A_llt.info() != Eigen::NumericalIssue
test. Consider the following example (numbers taken from Jiuzhang Suanshu,第 8 章,问题 1):
Eigen::Matrix3d A;
Eigen::Vector3d b;
Eigen::Vector3d x;
// A is full rank and all its eigen values >= 0
// However A is not symmetric, thus not PSD
A << 3, 2, 1,
2, 3, 1,
1, 2, 3;
b << 39, 34, 26;
// This alone doesn't check matrix symmetry, so can't guarantee PSD
Eigen::LLT<Eigen::Matrix3d> A_llt(A);
std::cout << (A_llt.info() == Eigen::NumericalIssue)
<< std::endl; // false, no issue detected
// ldlt solver requires PSD, wrong answer
x = A.ldlt().solve(b);
std::cout << x << std::endl; // Wrong solution [10.625, 1.5, 4.125]
std::cout << b.isApprox(A * x) << std::endl; // false
// ColPivHouseholderQR doesn't assume PSD, right answer
x = A.colPivHouseholderQr().solve(b);
std::cout << x << std::endl; // Correct solution [9.25, 4.25, 2.75]
std::cout << b.isApprox(A * x) << std::endl; // true
注意:更准确地说,可以通过检查 A
是对称的并且 A 的所有特征值 >= 0 来应用 definition of PSD。但是正如问题中提到的,这可能是计算上的贵。
您必须测试矩阵是否对称 (A.isApprox(A.transpose())
),然后创建 LDLT
(而不是 LLT,因为 LDLT 处理特征值之一为 0 的情况,即不是严格正向的),然后测试数值问题和正向性:
template <class MatrixT>
bool isPsd(const MatrixT& A) {
if (!A.isApprox(A.transpose())) {
return false;
}
const auto ldlt = A.template selfadjointView<Eigen::Upper>().ldlt();
if (ldlt.info() == Eigen::NumericalIssue || !ldlt.isPositive()) {
return false;
}
return true;
}
我在
上测试过这个
1 2
2 3
具有负特征值(因此不是 PSD)。如果没有 isPositive()
测试,isPsd()
错误地 returns 为真。
以后
1 2
2 4
具有空特征值(因此 PSD 但不是 PD)。
我正在实施谱聚类算法,我必须确保矩阵(拉普拉斯矩阵)是半正定矩阵。
检查矩阵是否为正定 (PD) 就足够了,因为可以在特征值中看到 "semi-" 部分。矩阵非常大(nxn,其中 n 是几千的数量级)所以特征分析很昂贵。
Eigen 中是否有任何检查在运行时给出 bool 结果?
如果矩阵不是 PD,Matlab 可以通过抛出异常来使用 chol()
方法给出结果。按照这个想法,Eigen returns 没有抱怨 LLL.llt().matrixL()
的结果,尽管我期待一些 warning/error。
Eigen 也有方法 isPositive
,但是由于 bug 它不能用于具有旧 Eigen 版本的系统。
您可以使用 Cholesky 分解 (LLT),其中 returns Eigen::NumericalIssue
如果矩阵为负,请参阅 documentation。
示例如下:
#include <Eigen/Dense>
#include <iostream>
#include <stdexcept>
int main()
{
Eigen::MatrixXd A(2, 2);
A << 1, 0 , 0, -1; // non semi-positive definitie matrix
std::cout << "The matrix A is" << std::endl << A << std::endl;
Eigen::LLT<Eigen::MatrixXd> lltOfA(A); // compute the Cholesky decomposition of A
if(lltOfA.info() == Eigen::NumericalIssue)
{
throw std::runtime_error("Possibly non semi-positive definitie matrix!");
}
}
除了@vsoftco 的回答外,我们还应检查矩阵对称性,因为 PD/PSD 的定义需要对称矩阵。
Eigen::LLT<Eigen::MatrixXd> A_llt(A);
if (!A.isApprox(A.transpose()) || A_llt.info() == Eigen::NumericalIssue) {
throw std::runtime_error("Possibly non semi-positive definitie matrix!");
}
这项检查很重要,例如一些 Eigen 求解器(如 LTDT) requires PSD(or NSD) matrix input. In fact, there exists non-symmetric and hence non-PSD matrix A
that passes the A_llt.info() != Eigen::NumericalIssue
test. Consider the following example (numbers taken from Jiuzhang Suanshu,第 8 章,问题 1):
Eigen::Matrix3d A;
Eigen::Vector3d b;
Eigen::Vector3d x;
// A is full rank and all its eigen values >= 0
// However A is not symmetric, thus not PSD
A << 3, 2, 1,
2, 3, 1,
1, 2, 3;
b << 39, 34, 26;
// This alone doesn't check matrix symmetry, so can't guarantee PSD
Eigen::LLT<Eigen::Matrix3d> A_llt(A);
std::cout << (A_llt.info() == Eigen::NumericalIssue)
<< std::endl; // false, no issue detected
// ldlt solver requires PSD, wrong answer
x = A.ldlt().solve(b);
std::cout << x << std::endl; // Wrong solution [10.625, 1.5, 4.125]
std::cout << b.isApprox(A * x) << std::endl; // false
// ColPivHouseholderQR doesn't assume PSD, right answer
x = A.colPivHouseholderQr().solve(b);
std::cout << x << std::endl; // Correct solution [9.25, 4.25, 2.75]
std::cout << b.isApprox(A * x) << std::endl; // true
注意:更准确地说,可以通过检查 A
是对称的并且 A 的所有特征值 >= 0 来应用 definition of PSD。但是正如问题中提到的,这可能是计算上的贵。
您必须测试矩阵是否对称 (A.isApprox(A.transpose())
),然后创建 LDLT
(而不是 LLT,因为 LDLT 处理特征值之一为 0 的情况,即不是严格正向的),然后测试数值问题和正向性:
template <class MatrixT>
bool isPsd(const MatrixT& A) {
if (!A.isApprox(A.transpose())) {
return false;
}
const auto ldlt = A.template selfadjointView<Eigen::Upper>().ldlt();
if (ldlt.info() == Eigen::NumericalIssue || !ldlt.isPositive()) {
return false;
}
return true;
}
我在
上测试过这个1 2
2 3
具有负特征值(因此不是 PSD)。如果没有 isPositive()
测试,isPsd()
错误地 returns 为真。
以后
1 2
2 4
具有空特征值(因此 PSD 但不是 PD)。