使用Eigen和Alglib一样快速获取行列式的对数

时间:2015-12-22 14:13:04

标签: performance eigen logarithm determinants alglib

我需要一种快速的方法来获得复数决定因素的对数,最好不要首先得到决定因素,然后再记录日志,因为数字可能变得非常大或非常小(后来我使用这些数字的比率,但仅限于它们是相似的;所以它们之间的差异表现得很好。)

到目前为止,我一直在使用alglib库;进行LU分解,然后沿对角线添加日志,然后添加i * pi乘以枢轴数。假设我的alglib::complex_2d_array m大小为n,我会

alglib::integer_1d_array pivots;
cmatrixlu(m, n, n, pivots);
int nopivs=0;
for(int j=0;j<n;j++) nopivs+=(pivots(j)!=j);
complex<double> aldet=0;
for(int i=0;i<n;i++) aldet+=log(m[i][i]);
aldet+=complex<double>(0, nopivs*pi);

我正在使用函数

complex<double> log(alglib::complex a) {return log(complex<double>(a.x,a.y);}

然而,在许多方面,特征库似乎很好;更容易使用,并使用complex<double>而不是自己的复杂类。此外,我已经将它用于其他目的,这样可以简化事情。

我尝试以类似的方式使用它,假设Eigen::MatrixXcd m的大小为n

Eigen::FullPivLU<Eigen::MatrixXcd> LU(m);
Eigen::MatrixXcd U=LU.matrixLU().triangularView<Eigen::Upper>();
complex<double> Edet=0;
for(int i=0;i<n;i++) Edet+=log(U(i,i));
Edet+=log(CD(LU.permutationP().determinant()*LU.permutationQ().determinant()));

然而,当我做一些测试时,Eigen的执行速度要慢得多。

所以我想知道是否有另一种方法可以更快地使用Eigen做到这一点?也许是另一种完全获取行列式日志的方法吗?

编辑:评论之后:这是我测试代码的方式:

int n=20, k=5000;
Eigen::MatrixXcd m(n, n);
srand((unsigned int) time(0));
m.setRandom();
alglib::complex_2d_array m2=Eigen2AL_2d(m);

Eigen::FullPivLU<Eigen::MatrixXcd> LU(m);
CD Edet=0.0, aldet=0.0, test=LU.determinant();

clock_t starttime=clock();
for(int i=0;i<k;i++) {
    Eigen::MatrixXcd m4=m;
    Eigen::FullPivLU<Eigen::MatrixXcd> LU(m4);
    Eigen::MatrixXcd U=LU.matrixLU().triangularView<Eigen::Upper>();
    Edet=0;
    for(int i=0;i<n;i++) Edet+=log(U(i,i));
        Edet+=log(CD(LU.permutationP().determinant()*LU.permutationQ().determinant()));
}
cout << "Eigen time: " << (clock()-starttime)/(double)CLOCKS_PER_SEC << endl;

starttime=clock();
for(int i=0;i<k;i++) {
     alglib::integer_1d_array pivots;
    alglib::complex_2d_array m3=m2;
    cmatrixlu(m3, n, n, pivots);
    int nopivs=0;
    for(int j=0;j<n;j++) nopivs+=(pivots(j)!=j);
    aldet=0;
    for(int i=0;i<n;i++) aldet+=log(m3[i][i]);
    aldet+=CD(0, nopivs*pi);
}
cout << "Alglib time: " << (clock()-starttime)/(double)CLOCKS_PER_SEC << endl;

cout << "det = " << test << " " << exp(aldet) << " " << exp(Edet) << endl;

使用g++ -c -std=c++11 -O2进行编译。典型的运行给出:

Eigen time: 2.10524
Alglib time: 0.664027

2 个答案:

答案 0 :(得分:2)

alglib中实现的LU算法仅执行部分旋转,因此,在Eigen中,您应该使用等效的PartialPivLU类,该类确实更快。此外,请确保在编译器优化的基础上进行测试。

答案 1 :(得分:1)

Eigen中的所有计算时间都花在LU分解上(在Eigen::FullPivLU<Eigen::MatrixXcd> LU(m4)时调用)。其余的可以忽略不计。你无法做很多改善,AFAIK。