当我尝试将 ATLAS 的方法clapack_sgesv
(相应的 FORTRAN 方法:sgesv.f)与以行主要存储顺序存储的矩阵一起使用时,我遇到了问题。
我在应用程序中将 Eigen3 用于大多数线性代数任务,但最近开始用调用 ATLAS 的 cblas 和 clapack 方法替换一些内部特征例程。我的应用程序必须支持通过定义 Eigen 的 EIGEN_DEFAULT_TO_ROW_MAJOR
标志将矩阵存储顺序切换到行主。这当然可以开箱即用地使用 Eigen 的方法,但clapack_
调用需要不同的代码路径。用 ATLAS 的 clapack_sgesv
方法替换 Eigen 的.partialPivLu().solve()
调用时遇到了问题。下面是说明该问题的最小代码示例:
#include <iostream>
#define EIGEN_DEFAULT_TO_ROW_MAJOR
#include <eigen3/Eigen/Eigen>
extern "C" {
#include <clapack.h>
}
using namespace std;
int main()
{
Eigen::MatrixXf A( 4, 4 );
A << 0.680375 , 0.823295 , -0.444451 , -0.270431 ,
-0.211234 , -0.604897 , 0.10794 , 0.0268018 ,
0.566198 , -0.329554 , -0.0452059 , 0.904459 ,
0.59688 , 0.536459 , 0.257742 , 0.83239 ;
Eigen::MatrixXf B( 4, 4 );
B << 0.271423 , -0.967399 , -0.686642 , 0.997849 ,
0.434594 , -0.514226 , -0.198111 , -0.563486 ,
-0.716795 , -0.725537 , -0.740419 , 0.0258648 ,
0.213938 , 0.608353 , -0.782382 , 0.678224 ;
cout << "----- Eigen" << endl;
cout << "A = " << endl << A << endl;
cout << "B = " << endl << B << endl;
Eigen::MatrixXf X = A.partialPivLu().solve( B );
cout << "X = " << endl << X << endl;
cout << "AX = " << endl << A * X << endl;
cout << "----- ATLAS" << endl;
Eigen::VectorXi ipiv( 4 );
clapack_sgesv(
#ifdef EIGEN_DEFAULT_TO_ROW_MAJOR
CblasRowMajor,
#else
CblasColMajor,
#endif
A.rows(),
B.cols(),
A.data(),
#ifdef EIGEN_DEFAULT_TO_ROW_MAJOR
A.cols(),
#else
A.rows(),
#endif
ipiv.data(),
B.data(),
#ifdef EIGEN_DEFAULT_TO_ROW_MAJOR
B.cols()
#else
B.rows()
#endif
);
cout << "piv = " << ipiv.transpose() << endl;
cout << "LU = " << endl << A << endl;
cout << "X =" << endl << B << endl;
return 0;
}
我用g++ -std=c++11 -Wall -Wextra -g -llapack -lcblas -latlas
编译这个.只要未定义EIGEN_DEFAULT_TO_ROW_MAJOR
,上述clapack_sgesv
调用就会给出与 Eigen 求解器相同的结果。
----- Eigen
A =
0.680375 0.823295 -0.444451 -0.270431
-0.211234 -0.604897 0.10794 0.0268018
0.566198 -0.329554 -0.0452059 0.904459
0.59688 0.536459 0.257742 0.83239
B =
0.271423 -0.967399 -0.686642 0.997849
0.434594 -0.514226 -0.198111 -0.563486
-0.716795 -0.725537 -0.740419 0.0258648
0.213938 0.608353 -0.782382 0.678224
X =
4.29176 -3.45693 -3.46864 0.547927
-1.3688 2.04333 1.13806 0.735351
5.6716 -0.593909 -2.65158 -0.0154493
-3.69446 2.07672 1.6349 -0.0472447
AX =
0.271423 -0.967399 -0.686642 0.997849
0.434594 -0.514226 -0.198111 -0.563486
-0.716796 -0.725537 -0.740419 0.0258648
0.213938 0.608353 -0.782382 0.678224
----- ATLAS
piv = 0 2 3 3
LU =
0.680375 0.823295 -0.444451 -0.270431
0.832185 -1.01469 0.32466 1.12951
0.877281 0.183112 0.588201 0.862807
-0.310467 0.344235 -0.241085 -0.237964
X =
4.29176 -3.45694 -3.46864 0.547927
-1.3688 2.04333 1.13806 0.735351
5.6716 -0.593909 -2.65158 -0.0154493
-3.69446 2.07672 1.6349 -0.0472447
如果我定义它,ATLAS的结果是错误的。
----- Eigen
[... same as above ...]
----- ATLAS
piv = 1 1 3 3
LU =
0.823295 0.826405 -0.328474 -0.539844
-0.604897 0.288656 -0.595488 -0.757338
-0.329554 0.838543 1.29555 0.31797
0.536459 0.153548 1.10004 0.313854
X =
-2.21567 2.33841 -0.554441 1.45218
-2.60368 1.14776 -3.83383 1.63747
-5.05167 2.4991 -3.36881 3.08596
6.03571 -1.84576 8.32067 -4.90008
我的第一个怀疑当然是我在clapack_sgesv()
电话中搞砸了什么。但是,除了设置存储顺序并将前导维度从行数切换到列数之外,似乎没有必要。
我注意到的另一件非常令人困惑的事情如下:当我尝试仅解决单个右侧时,clapack_sgesv()
调用失败并显示Parameter 8 to routine clapack_sgesv was incorrect, ldb must be >= MAX(N,1): ldb=1 N=4
。这在数学上没有任何意义。
我怀疑我的错误一定很明显,但我没有看到。
我的clapack_sgesv()
调用有什么问题导致它在行主存储顺序中失败?
我发现了我的错误。正如 ATLAS FAQ 中所解释的,右侧不被视为矩阵,而是内存中相邻的列向量的集合。如果存储顺序是列主存储顺序,这不会有区别,但对于行主存储顺序,这不会有区别,因为列向量的元素在内存中不再相邻。如果始终以列主格式存储 RHS 和解决方案"矩阵",则它可以工作。