【问题标题】:Understanding LAPACK calls in C++ with a simple example用一个简单的例子理解 C++ 中的 LAPACK 调用
【发布时间】:2012-04-11 18:53:33
【问题描述】:

我是 LAPACK 和 C++/Fortran 接口的初学者。我需要在 Mac OS-X Lion 上使用 LAPACK/BLAS 解决线性方程和特征值问题。 OS-X Lion 提供了优化的 BLAS 和 LAPACK 库(在 /usr/lib 中),我正在链接这些库,而不是从 netlib 下载它们。

我的程序(转载如下)正在编译和运行良好,但它给了我错误的答案。我在网络和 Stackoverflow 中进行了研究,问题可能必须处理 C++ 和 Fortran 如何以不同格式存储数组(行专业与列专业)。但是,正如您将在我的示例中看到的那样,我的示例的简单数组在 C++ 和 fortran 中应该看起来相同。反正就这样吧。

假设我们要解决以下线性系统:

x + y = 2

x - y = 0

解是 (x,y) = (1,1)。现在我尝试使用 Lapack 解决这个问题,如下所示

// LAPACK test code

#include<iostream>
#include<vector>

using namespace std;
extern "C" void dgetrs(char *TRANS, int *N, int *NRHS, double *A, 
                      int *LDA, int *IPIV, double *B, int *LDB, int *INFO );

int main()
{
    char trans = 'N';
    int dim = 2;    
    int nrhs = 1;
    int LDA = dim;
    int LDB = dim;
    int info;

    vector<double> a, b;

    a.push_back(1);
    a.push_back(1);
    a.push_back(1);
    a.push_back(-1);

    b.push_back(2);
    b.push_back(0);

    int ipiv[3];


    dgetrs(&trans, &dim, &nrhs, & *a.begin(), &LDA, ipiv, & *b.begin(), &LDB, &info);


    std::cout << "solution is:";    
    std::cout << "[" << b[0] << ", " << b[1] << ", " << "]" << std::endl;
    std::cout << "Info = " << info << std::endl; 

    return(0);
}

这段代码编译如下:

g++ -Wall -llapack -lblas lapacktest.cpp

但是,在运行它时,我得到的解决方案是 (-2,2),这显然是错误的。我已经尝试了矩阵a 的所有行/列重新排列组合。还要注意a 的矩阵表示在行和列格式上应该是相同的。我认为让这个简单的例子工作将使我开始使用 LAPACK,任何帮助将不胜感激。

【问题讨论】:

  • 您使用的是什么 lapack 库,是 64 位代码吗?
  • 我正在使用 Mac OS-X Lion 上原生的 /usr/lib/liblapack.dylib 和 /usr/lib/libblas.dylib。我没有安装任何外部 LAPACK/BLAS 库。
  • 在你的例子中,你正在求解一个对称矩阵,所以无论你有行优先还是列优先,你都不会看到任何区别。

标签: c++ fortran lapack


【解决方案1】:

在使用dgetrs 求解系统之前,您需要分解 矩阵(通过调用dgetrf)。或者,您可以使用 dgesv 例程,它会为您完成这两个步骤。

顺便说一句,您不需要自己声明接口,它们在 Accelerate 标头中:

// LAPACK test code

#include <iostream>
#include <vector>
#include <Accelerate/Accelerate.h>

using namespace std;

int main()
{
    char trans = 'N';
    int dim = 2;    
    int nrhs = 1;
    int LDA = dim;
    int LDB = dim;
    int info;

    vector<double> a, b;

    a.push_back(1);
    a.push_back(1);
    a.push_back(1);
    a.push_back(-1);

    b.push_back(2);
    b.push_back(0);

    int ipiv[3];

    dgetrf_(&dim, &dim, &*a.begin(), &LDA, ipiv, &info);
    dgetrs_(&trans, &dim, &nrhs, & *a.begin(), &LDA, ipiv, & *b.begin(), &LDB, &info);


    std::cout << "solution is:";    
    std::cout << "[" << b[0] << ", " << b[1] << ", " << "]" << std::endl;
    std::cout << "Info = " << info << std::endl; 

    return(0);
}

【讨论】:

  • 斯蒂芬,非常感谢。这行得通。顺便说一句,我希望你不介意回答两个后续问题:(1)我在哪里可以找到指定所有依赖项的 LAPACK 文档(例如在 dgetrs 之前使用 dgetrf)。例如,我通过在 Netlib 中的 dgetrs() 函数中查找信息来构建我的原始程序。但是,它并没有说我首先必须使用 dgetrf 进行分解。 (2) 我假设要使用 Accelerate 框架,我只需使用 -framework Accelerate 进行编译。那是对的吗?再次感谢。
  • @RDK:您可以使用 -framework Accelerate 进行编译,也可以像您一直在做的那样直接针对 LAPACK/BLAS 进行链接(无论哪种方式,您最终都会获得相同的 LAPACK 库)。查看 netlib 上的 dgetrs 实际上 确实 告诉您您需要 dgetrf:“DGETRS 使用 DGETRF 计算的 LU 分解求解具有一般 N×N 矩阵 A 的线性方程组。”但是,更好的参考是 LAPACK 用户指南,它可以在 netlib 上以 html 的形式获得,也可以以廉价的死树形式获得。
  • 你是对的。确实是这么说的。由坏和道歉。我想我将来必须更仔细地阅读。我正在寻找便宜的死树形式,因为我的同事也会对手头的参考感兴趣。再次感谢您的耐心和回复。
  • @RDK:不用道歉,那段文字充其量是令人困惑的。
【解决方案2】:

对于那些不想使用 Accelerate Framework 的人,我提供了 Stephen Canon 的代码(当然要感谢他),除了纯库链接之外什么都没有

// LAPACK test code
//compile with: g++ main.cpp -llapack -lblas -o testprog

#include <iostream>
#include <vector>

using namespace std;

extern "C" void dgetrf_(int* dim1, int* dim2, double* a, int* lda, int* ipiv, int* info);
extern "C" void dgetrs_(char *TRANS, int *N, int *NRHS, double *A, int *LDA, int *IPIV, double *B, int *LDB, int *INFO );

int main()
{
    char trans = 'N';
    int dim = 2;
    int nrhs = 1;
    int LDA = dim;
    int LDB = dim;
    int info;

    vector<double> a, b;

    a.push_back(1);
    a.push_back(1);
    a.push_back(1);
    a.push_back(-1);

    b.push_back(2);
    b.push_back(0);

    int ipiv[3];

    dgetrf_(&dim, &dim, &*a.begin(), &LDA, ipiv, &info);
    dgetrs_(&trans, &dim, &nrhs, & *a.begin(), &LDA, ipiv, & *b.begin(), &LDB, &info);


    std::cout << "solution is:";
    std::cout << "[" << b[0] << ", " << b[1] << ", " << "]" << std::endl;
    std::cout << "Info = " << info << std::endl;

    return(0);
}

关于手册,英特尔网站上有完整的 PDF 版本。这是他们的 HTML 文档示例。

http://software.intel.com/sites/products/documentation/hpc/mkl/mklman/GUID-A02DB70F-9704-42A4-9071-D409D783D911.htm

【讨论】:

  • 这个代码示例很有帮助。帮助我认识到我的 LAPACK 库副本未正确链接到我的项目。
【解决方案3】:

如果你想从 C++ 中使用 LAPACK,你可能想看看FLENS。它定义了 LAPACK 的低级和高级接口,但也重新实现了一些 LAPACK 功能。

通过低级 FLENS-LAPACK 接口,您可以使用自己的矩阵/向量类型(如果它们具有符合 LAPACK 的内存布局)。您对dgetrf 的调用如下所示:

info = lapack::getrf(NoTrans, dim, nrhs, a.begin(), LDA, ipiv);

对于dgetrs

lapack::getrs(NoTrans, dim, nrhs, a.begin(), LDA, ipiv, b.begin(), LDB);

因此,低级 FLENS-LAPACK 函数在元素类型方面是重载的。因此LAPACK函数sgetrsdgetrscgetrszgetrs在FLENS-LAPACK的低级接口lapack::getrs中。您还可以通过值/引用而不是指针传递参数(例如 LDA 而不是 &amp;LDA)。

如果您使用 FLENS 矩阵类型,您可以将其编码为

info = lapack::trf(NoTrans, A, ipiv);
if (info==0) {
    lapack::trs(NoTrans, A, ipiv, b);
}

或者你只是使用LAPACK驱动函数dgesv

lapack::sv(NoTrans, A, ipiv, b);

这里有一个list of FLENS-LAPACK 驱动函数。

免责声明:是的,FLENS 是我的宝贝!这意味着我编写了大约 95% 的代码,每一行代码都是值得的。

【讨论】:

  • FLENS 看起来是连接 LAPACK/C++ 的好方法。我一定会检查一下。
猜你喜欢
  • 1970-01-01
  • 2013-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-21
  • 2012-11-25
  • 1970-01-01
  • 2015-09-30
相关资源
最近更新 更多