如何开始在C ++中使用LAPACK?


10

我是计算机科学的新手,我已经学习了积分,插值的基本方法,以及C ++上的RK4,Numerov等方法,但是最近我的教授要求我学习如何使用LAPACK解决与矩阵有关的问题。例如,寻找复杂矩阵的特征值。我从未使用过第三方库,并且几乎总是编写自己的函数。我已经搜寻了几天,但找不到任何适合lapack的业余爱好者指南。所有这些都是用我不理解的语言编写的,我也不知道为什么使用已经编写的函数应该这么复杂。他们到处都是zgeev,dtrsv等单词,我很沮丧。我只想编写类似以下伪代码的代码:

#include <lapack:matrix>
int main(){
  LapackComplexMatrix A(n,n);
  for...
   for...
    cin>>A(i,j);
  cout<<LapackEigenValues(A);
  return 0;
}

我不知道我是傻还是业余。但是,这不应该那么难吗?我什至不知道我应该使用LAPACK还是LAPACK ++。(我用C ++编写代码,不了解Python或FORTRAN)以及如何安装它们。


也许这个例子会很有用:matrixprogramming.com/files/code/LAPACK
nukeguy

如果您才刚刚开始,也许使用一个更简单的库(例如ArrayFire github.com/arrayfire/arrayfire)会更容易。您可以直接从C ++调用它,而API则更简单,我认为它可以执行LAPACK的所有操作。
维克拉姆

在另一篇文章中,用户提出了自己的包装器FLENS,该包装器具有非常好的语法,可以简化您对LAPACK的介绍。
Zythos '18年

直接调用LAPACK函数非常繁琐且容易出错。有几种用于LAPACK的用户友好的C ++包装器,它们提供了更简单的用法,例如Armadillo。有关复杂本征分解的特定用例,请参见用户友好的eig_gen()函数,该函数在下面封装了LAPACK怪兽zheev(JOBZ,UPLO,N,A,LDA,W,WORK,LWORK,RWORK,INFO),并将获得的特征值和特征向量重新格式化为标准表示。
hbrerkere

Answers:


18

我将不同意其他一些答案,并说我相信弄清楚如何使用LAPACK 在科学计算领域重要。

但是,使用LAPACK的学习曲线很大。这是因为它写得很低。这样做的缺点是,它看起来非常神秘,并且感觉不愉快。它的优点是接口是明确的,并且基本上不会改变。此外,LAPACK的实现(例如英特尔数学内核库)确实非常快。

出于我自己的目的,我有自己的高级C ++类,它们围绕LAPACK子例程进行包装。许多科学图书馆也在下面使用LAPACK。有时,仅使用它们会更容易,但是我认为理解下面的工具有很多价值。为此,我提供了一个使用LAPACK用C ++编写的小型工作示例,以帮助您入门。这在Ubuntu中工作,liblapack3安装了软件包,以及用于构建的其他必需软件包。它可能可以在大多数Linux发行版中使用,但是LAPACK的安装和链接可能有所不同。

这是文件 test_lapack.cpp

#include <iostream>
#include <fstream>


using namespace std;

// dgeev_ is a symbol in the LAPACK library files
extern "C" {
extern int dgeev_(char*,char*,int*,double*,int*,double*, double*, double*, int*, double*, int*, double*, int*, int*);
}

int main(int argc, char** argv){

  // check for an argument
  if (argc<2){
    cout << "Usage: " << argv[0] << " " << " filename" << endl;
    return -1;
  }

  int n,m;
  double *data;

  // read in a text file that contains a real matrix stored in column major format
  // but read it into row major format
  ifstream fin(argv[1]);
  if (!fin.is_open()){
    cout << "Failed to open " << argv[1] << endl;
    return -1;
  }
  fin >> n >> m;  // n is the number of rows, m the number of columns
  data = new double[n*m];
  for (int i=0;i<n;i++){
    for (int j=0;j<m;j++){
      fin >> data[j*n+i];
    }
  }
  if (fin.fail() || fin.eof()){
    cout << "Error while reading " << argv[1] << endl;
    return -1;
  }
  fin.close();

  // check that matrix is square
  if (n != m){
    cout << "Matrix is not square" <<endl;
    return -1;
  }

  // allocate data
  char Nchar='N';
  double *eigReal=new double[n];
  double *eigImag=new double[n];
  double *vl,*vr;
  int one=1;
  int lwork=6*n;
  double *work=new double[lwork];
  int info;

  // calculate eigenvalues using the DGEEV subroutine
  dgeev_(&Nchar,&Nchar,&n,data,&n,eigReal,eigImag,
        vl,&one,vr,&one,
        work,&lwork,&info);


  // check for errors
  if (info!=0){
    cout << "Error: dgeev returned error code " << info << endl;
    return -1;
  }

  // output eigenvalues to stdout
  cout << "--- Eigenvalues ---" << endl;
  for (int i=0;i<n;i++){
    cout << "( " << eigReal[i] << " , " << eigImag[i] << " )\n";
  }
  cout << endl;

  // deallocate
  delete [] data;
  delete [] eigReal;
  delete [] eigImag;
  delete [] work;


  return 0;
}

可以使用命令行来构建

g++ -o test_lapack test_lapack.cpp -llapack

这将产生一个名为的可执行文件test_lapack。我已经将其设置为读取文本输入文件。这是一个名为matrix.txt3x3矩阵的文件。

3 3
-1.0 -8.0  0.0
-1.0  1.0 -5.0
 3.0  0.0  2.0

要运行该程序,只需键入

./test_lapack matrix.txt

在命令行上,输出应为

--- Eigenvalues ---
( 6.15484 , 0 )
( -2.07742 , 3.50095 )
( -2.07742 , -3.50095 )

注释:

  • 您似乎不喜欢LAPACK的命名方案。这里有一个简短的描述。
  • DGEEV子例程的接口在此处。您应该能够将此处的参数描述与我在这里所做的进行比较。
  • 请注意extern "C"顶部的部分,并且我已在上添加下划线dgeev_。那是因为该库是用Fortran编写和构建的,因此在链接时使符号匹配是必要的。这取决于编译器和系统,因此,如果在Windows上使用它,则都必须更改。
  • 有人可能会建议对LAPACK使用C接口。他们可能是对的,但我一直都是这样做的。

3
一些快速的Googlage可找到您要查找的许多内容。也许您只是不确定要搜索什么。Netlib是LAPACK的守护者。该文档可在此处找到。该页面提供了LAPACK主要功能的便捷表格。一些重要的问题是(1)方程组的求解;(2)特征值问题;(3)奇异值分解;以及(4)QR分解。您了解DGEEV的手册吗?
LedHead

1
它们只是同一事物的不同接口。LAPACK是原始的。它是用Fortran编写的,因此要使用它,您必须玩一些游戏才能从C / C ++进行交叉编译,如我所展示的。我从没使用过LAPACKE,但看起来它是LAPACK上相当薄的C包装程序,避免了这种交叉编译业务,但它仍然是低级的。LAPACK ++似乎是更高级别的C ++包装器,但我认为它甚至不再受支持(如果我错了,请纠正我)。
LedHead

1
我不知道任何特定的代码集合。但是,如果您使用Google的任何LAPACK子例程名称,都会在其中一个StackExchange网站上找到一个老问题。
LedHead

1
@AlirezaHashemi顺便说一句,您必须提供WORK数组的原因是因为LAPACK通常不会在其子例程中分配任何内存。如果我们使用的是LAPACK,则很可能会使用内存块,并且分配内存的成本很高,因此让调用例程负责内存分配是很有意义的。由于DGEEV需要内存来存储中间数量,因此我们必须为其提供工作空间。
LedHead

1
得到它了。而且我成功编写了我的第一个代码,使用zgeev计算复杂矩阵的特征值。并且已经做得更多!谢谢!
Alireza

7

我通常不告诉别人我应该怎么做,而不是回答他们的问题,但是在这种情况下,我将作一个例外。

Lapack用FORTRAN编写,API非常类似于FORTRAN。Lapack提供了一个C API,使接口的痛苦减轻了一点,但是使用C ++中的Lapack绝不会是一种愉快的体验。

另外,还有一个名为Eigen的C ++矩阵类库,它具有Lapack的许多功能,可提供与更好的Lapack实现相当的计算性能,并且在C ++中使用非常方便。特别是,这是使用Eigen编写示例代码的方式

#include <iostream>
using std::cout;
using std::endl;

#include <Eigen/Eigenvalues>

int main()
{
  const int n = 4;
  Eigen::MatrixXd a(n, n);
  a <<
    0.35, 0.45, -0.14, -0.17,
    0.09, 0.07, -0.54, 0.35,
    -0.44, -0.33, -0.03, 0.17,
    0.25, -0.32, -0.13, 0.11;
  Eigen::EigenSolver<Eigen::MatrixXd> es;
  es.compute(a);
  Eigen::VectorXcd ev = es.eigenvalues();
  cout << ev << endl;
}

此示例特征值问题是Lapack函数的测试用例 dgeev。您可以查看FORTRAN代码和此问题dgeev示例的结果, 并进行自己的比较。


感谢您的回答和解释!我将尝试该库并选择最适合我的需求的库。
Alireza

哦,他们超载operator,!从未见过在实际操作中做到这一点:-)
Wolfgang Bangerth,2017年

1
实际上,与operator,第一次出现相比,这种过载更有趣/更好。它用于初始化矩阵。初始化矩阵的条目可以是标量常量,也可以是先前定义的矩阵或子矩阵。非常像MATLAB。希望我的C ++编程能力足够好,可以实现使我自己复杂的事情;-)
比尔·格林

7

这是与上述相同的另一个答案。

您应该研究Armadillo C ++线性代数库

优点:

  1. 函数语法是高级的(类似于MATLAB的语法)。因此DGESV,就没有庞然大物了X = solve( A, B )(尽管在那些看起来奇怪的LAPACK函数名后面一个原因 ……)。
  2. 实现各种矩阵分解(LU,QR,特征值,SVD,Cholesky等)
  3. 它是快速使用得当。
  4. 据可查
  5. 支持稀疏矩阵(稍后您将要进行研究)。
  6. 您可以将其链接到超级优化的BLAS / LAPACK库,以获得最佳性能。

这就是@BillGreene的代码在Armadillo中的样子:

#include <iostream>
#include <armadillo>

using namespace std;
using namespace arma;

int main()
{
   const int k = 4;
   mat A = zeros<mat>(k,k) // mat == Mat<double>

   // with the << operator...
   A <<
    0.35 << 0.45 << -0.14 << -0.17 << endr
    0.09 << 0.07 << -0.54 << 0.35  << endr
    -0.44 << -0.33 << -0.03 << 0.17 << endr
    0.25 << -0.32 << -0.13 << 0.11 << endr;

   // but using an initializer list is faster
   A = { {0.35, 0.45, -0.14, -0.17}, 
         {0.09, 0.07, -0.54, 0.35}, 
         {-0.44, -0.33, -0.03, 0.17}, 
         {0.25, -0.32, -0.13, 0.11} };

   cx_vec eigval; // eigenvalues may well be complex
   cx_mat eigvec;

   // eigenvalue decomposition for general dense matrices
   eig_gen(eigval, eigvec, A);

   std::cout << eigval << std::endl;

   return 0;
}

感谢您的回答和解释!我将尝试该库并选择最适合我的需求的库。
Alireza
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.