具有行主要存储顺序的 ATLAS clapack_sgesv

ATLAS' clapack_sgesv with row-major storage order

本文关键字:ATLAS clapack sgesv 顺序 存储      更新时间:2023-10-16

当我尝试将 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 和解决方案"矩阵",则它可以工作。