2010-08-19 439 views
18

我希望能够使用lapack在C/C++中计算一般的NxN矩阵的逆矩阵。使用lapack在C/C++中计算矩阵的逆矩阵

我的理解是,在lapack中做倒置的方法是使用dgetri函数,但是,我无法弄清楚它的所有参数应该是什么。

下面是我的代码有:

void dgetri_(int* N, double* A, int* lda, int* IPIV, double* WORK, int* lwork, int* INFO); 

int main(){ 

    double M [9] = { 
     1,2,3, 
     4,5,6, 
     7,8,9 
    }; 

    return 0; 
} 

你将如何完成它使用dgetri_获得3x3矩阵M的逆?

回答

16

首先,M必须是一个二维数组,如double M[3][3]。从数学上讲,你的数组是一个1x9向量,它不可逆。

  • N是一个指针,指向用于 顺序矩阵的一个int - 在这种情况下, N = 3。

  • A是一个指向矩阵的LU 分解,这 您可以通过运行LAPACK 常规dgetrf得到。

  • LDA是矩阵,它可以让你 挑选出一个更大的 矩阵的一个子集,如果你想只是一个倒置小 一块的“龙头 元素”的整数。如果要反转 整个矩阵,LDA应该只是 等于N.

  • IPIV是 矩阵的枢轴指数,换句话说,这是一个什么样的行的指令列表 交换 以反转矩阵。 IPIV 应该由LAPACK 例程dgetrf生成。

  • LWORK和WORK是LAPACK使用的“工作空间” 。如果将整个矩阵逆转为 ,则LWORK应该是等于N^2的 int,并且WORK应该是 带有LWORK元素的双数组。

  • INFO只是一个状态变量,以 告诉你操作 是否成功完成。由于不是所有的 矩阵都是可逆的,所以我会推荐你​​把这个发送给一些 类型的错误检查系统。成功操作时INFO = 0,如果第i个参数的输入值不正确,则INFO = -i,如果矩阵不可逆,则INFO> 0。

因此,对于您的代码,我会做这样的事情:

int main(){ 

    double M[3][3] = { {1 , 2 , 3}, 
         {4 , 5 , 6}, 
         {7 , 8 , 9}} 
    double pivotArray[3]; //since our matrix has three rows 
    int errorHandler; 
    double lapackWorkspace[9]; 

    // dgetrf(M,N,A,LDA,IPIV,INFO) means invert LDA columns of an M by N matrix 
    // called A, sending the pivot indices to IPIV, and spitting error 
    // information to INFO. 
    // also don't forget (like I did) that when you pass a two-dimensional array 
    // to a function you need to specify the number of "rows" 
    dgetrf_(3,3,M[3][],3,pivotArray[3],&errorHandler); 
    //some sort of error check 

    dgetri_(3,M[3][],3,pivotArray[3],9,lapackWorkspace,&errorHandler); 
    //another error check 

    } 
+18

关于1X9或3×3。内存布局方面确实没有任何区别。事实上,BLAS/LAPACK例程不需要2d阵列。他们采用一维数组,并假设你如何布置它。请注意BLAS和LAPACK将假设FORTRAN排序(行更改最快)而不是C排序。 – MRocklin 2012-10-18 18:28:09

+0

您可能需要'LAPACKE_dgetrf'而不是fortran例程'dgetrf_'。同上'dgetri'。否则你必须使用地址来调用所有内容。 – 2016-05-20 02:32:48

19

下面是在C/C++使用LAPACK计算矩阵的逆工作代码:

#include <cstdio> 

extern "C" { 
    // LU decomoposition of a general matrix 
    void dgetrf_(int* M, int *N, double* A, int* lda, int* IPIV, int* INFO); 

    // generate inverse of a matrix given its LU decomposition 
    void dgetri_(int* N, double* A, int* lda, int* IPIV, double* WORK, int* lwork, int* INFO); 
} 

void inverse(double* A, int N) 
{ 
    int *IPIV = new int[N+1]; 
    int LWORK = N*N; 
    double *WORK = new double[LWORK]; 
    int INFO; 

    dgetrf_(&N,&N,A,&N,IPIV,&INFO); 
    dgetri_(&N,A,&N,IPIV,WORK,&LWORK,&INFO); 

    delete IPIV; 
    delete WORK; 
} 

int main(){ 

    double A [2*2] = { 
     1,2, 
     3,4 
    }; 

    inverse(A, 2); 

    printf("%f %f\n", A[0], A[1]); 
    printf("%f %f\n", A[2], A[3]); 

    return 0; 
} 
+2

您不需要为IPIV变量分配N + 1(但只有N个无符号)int。此外,我不建议使用这种函数来计算倍数反转。只分配一次所有需要的数据,最后只需分配数据。 – matovitch 2013-11-22 11:09:27

+0

你可能需要'delete [] IPIV'和'delete [] work'。 – 2016-05-20 02:13:54

+1

没有语言C/C++。你展示的代码是C++。但问题是关于C. – Olaf 2016-06-23 20:40:03

0

下面是Spencer Nelson上面的示例的工作版本。关于它的一个谜就是输入矩阵按照行优先顺序排列,即使它看起来叫做底层Fortran例程dgetri。我被引导认为所有Fortran的例程都需要列主要的顺序,但我并不是LAPACK方面的专家,事实上,我正在使用这个例子来帮助我学习它。但是,抛开那个谜团:

示例中的输入矩阵是单数。 LAPACK试图告诉你,通过在errorHandler中返回一个3。我将该矩阵中的9更改为19,获得errorHandler0信号成功,并将结果与​​Mathematica进行比较。如上所述,该比较也是成功的,并且证实了示例中的矩阵应该按行优先顺序排列。

这里是工作代码:

#include <stdio.h> 
#include <stddef.h> 
#include <lapacke.h> 

int main() { 
    int N = 3; 
    int NN = 9; 
    double M[3][3] = { {1 , 2 , 3}, 
         {4 , 5 , 6}, 
         {7 , 8 , 9} }; 
    int pivotArray[3]; //since our matrix has three rows 
    int errorHandler; 
    double lapackWorkspace[9]; 

    // dgetrf(M,N,A,LDA,IPIV,INFO) means invert LDA columns of an M by N matrix 
    // called A, sending the pivot indices to IPIV, and spitting error information 
    // to INFO. also don't forget (like I did) that when you pass a two-dimensional 
    // array to a function you need to specify the number of "rows" 
    dgetrf_(&N, &N, M[0], &N, pivotArray, &errorHandler); 
    printf ("dgetrf eh, %d, should be zero\n", errorHandler); 

    dgetri_(&N, M[0], &N, pivotArray, lapackWorkspace, &NN, &errorHandler); 
    printf ("dgetri eh, %d, should be zero\n", errorHandler); 

    for (size_t row = 0; row < N; ++row) 
    { for (size_t col = 0; col < N; ++col) 
     { printf ("%g", M[row][col]); 
      if (N-1 != col) 
      { printf (", "); } } 
     if (N-1 != row) 
     { printf ("\n"); } } 

    return 0; } 

我建立并运行它在Mac上,如下所示:

gcc main.c -llapacke -llapack 
./a.out 

我做的LAPACKE库中nm,发现如下:

liblapacke.a(lapacke_dgetri.o): 
       U _LAPACKE_dge_nancheck 
0000000000000000 T _LAPACKE_dgetri 
       U _LAPACKE_dgetri_work 
       U _LAPACKE_xerbla 
       U _free 
       U _malloc 

liblapacke.a(lapacke_dgetri_work.o): 
       U _LAPACKE_dge_trans 
0000000000000000 T _LAPACKE_dgetri_work 
       U _LAPACKE_xerbla 
       U _dgetri_ 
       U _free 
       U _malloc 

它看起来像有一个LAPACKE [原文如此]包装,可能会缓解我们不得不为了fortran的方便而到处寻址,但我可能不会试着去尝试它,因为我有前进的方向。

EDIT

这里是绕过LAPACKE [原文如此],直接使用LAPACK Fortran例程工作版本。我不明白为什么行主要输入会产生正确的结果,但我在Mathematica中再次证实了它。

#include <stdio.h> 
#include <stddef.h> 

int main() { 
    int N = 3; 
    int NN = 9; 
    double M[3][3] = { {1 , 2 , 3}, 
         {4 , 5 , 6}, 
         {7 , 8 , 19} }; 
    int pivotArray[3]; //since our matrix has three rows 
    int errorHandler; 
    double lapackWorkspace[9]; 
    /* from http://www.netlib.no/netlib/lapack/double/dgetrf.f 
     SUBROUTINE DGETRF(M, N, A, LDA, IPIV, INFO) 
     * 
     * -- LAPACK routine (version 3.1) -- 
     *  Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. 
     *  November 2006 
     * 
     *  .. Scalar Arguments .. 
     INTEGER   INFO, LDA, M, N 
     *  .. 
     *  .. Array Arguments .. 
     INTEGER   IPIV(*) 
     DOUBLE PRECISION A(LDA, *) 
    */ 

    extern void dgetrf_ (int * m, int * n, double * A, int * LDA, int * IPIV, 
         int * INFO); 

    /* from http://www.netlib.no/netlib/lapack/double/dgetri.f 
     SUBROUTINE DGETRI(N, A, LDA, IPIV, WORK, LWORK, INFO) 
     * 
     * -- LAPACK routine (version 3.1) -- 
     *  Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. 
     *  November 2006 
     * 
     *  .. Scalar Arguments .. 
     INTEGER   INFO, LDA, LWORK, N 
     *  .. 
     *  .. Array Arguments .. 
     INTEGER   IPIV(*) 
     DOUBLE PRECISION A(LDA, *), WORK(*) 
    */ 

    extern void dgetri_ (int * n, double * A, int * LDA, int * IPIV, 
         double * WORK, int * LWORK, int * INFO); 

    // dgetrf(M,N,A,LDA,IPIV,INFO) means invert LDA columns of an M by N matrix 
    // called A, sending the pivot indices to IPIV, and spitting error information 
    // to INFO. also don't forget (like I did) that when you pass a two-dimensional 
    // array to a function you need to specify the number of "rows" 
    dgetrf_(&N, &N, M[0], &N, pivotArray, &errorHandler); 
    printf ("dgetrf eh, %d, should be zero\n", errorHandler); 

    dgetri_(&N, M[0], &N, pivotArray, lapackWorkspace, &NN, &errorHandler); 
    printf ("dgetri eh, %d, should be zero\n", errorHandler); 

    for (size_t row = 0; row < N; ++row) 
    { for (size_t col = 0; col < N; ++col) 
     { printf ("%g", M[row][col]); 
      if (N-1 != col) 
      { printf (", "); } } 
     if (N-1 != row) 
     { printf ("\n"); } } 
    return 0; } 

构建并运行这样的:

$ gcc foo.c -llapack 
$ ./a.out 
dgetrf eh, 0, should be zero 
dgetri eh, 0, should be zero 
-1.56667, 0.466667, 0.1 
1.13333, 0.0666667, -0.2 
0.1, -0.2, 0.1 

编辑

谜似乎不再是一个谜。我认为计算是按照列主要的顺序完成的,因为它们必须这样做,但我同时输入和打印这些矩阵,就好像它们是按照主要顺序排列的一样。我有两个互相抵消的错误,所以事情看起来很划算,尽管它们是列式的。

2

以上是使用OpenBlas接口到LAPACKE的上述工作版本。 链路与openblas库(LAPACKE已经包含)

#include <stdio.h> 
#include "cblas.h" 
#include "lapacke.h" 

// inplace inverse n x n matrix A. 
// matrix A is Column Major (i.e. firts line, second line ... *not* C[][] order) 
// returns: 
// ret = 0 on success 
// ret < 0 illegal argument value 
// ret > 0 singular matrix 

lapack_int matInv(double *A, unsigned n) 
{ 
    int ipiv[n+1]; 
    lapack_int ret; 

    ret = LAPACKE_dgetrf(LAPACK_COL_MAJOR, 
          n, 
          n, 
          A, 
          n, 
          ipiv); 

    if (ret !=0) 
     return ret; 


    ret = LAPACKE_dgetri(LAPACK_COL_MAJOR, 
         n, 
         A, 
         n, 
         ipiv); 
    return ret; 
} 

int main() 
{ 
    double A[] = { 
     0.378589, 0.971711, 0.016087, 0.037668, 0.312398, 
     0.756377, 0.345708, 0.922947, 0.846671, 0.856103, 
     0.732510, 0.108942, 0.476969, 0.398254, 0.507045, 
     0.162608, 0.227770, 0.533074, 0.807075, 0.180335, 
     0.517006, 0.315992, 0.914848, 0.460825, 0.731980 
    }; 

    for (int i=0; i<25; i++) { 
     if ((i%5) == 0) putchar('\n'); 
     printf("%+12.8f ",A[i]); 
    } 
    putchar('\n'); 

    matInv(A,5); 

    for (int i=0; i<25; i++) { 
     if ((i%5) == 0) putchar('\n'); 
     printf("%+12.8f ",A[i]); 
    } 
    putchar('\n'); 
} 

实施例:

% g++ -I [OpenBlas path]/include/ example.cpp [OpenBlas path]/lib/libopenblas.a 
% a.out 

+0.37858900 +0.97171100 +0.01608700 +0.03766800 +0.31239800 
+0.75637700 +0.34570800 +0.92294700 +0.84667100 +0.85610300 
+0.73251000 +0.10894200 +0.47696900 +0.39825400 +0.50704500 
+0.16260800 +0.22777000 +0.53307400 +0.80707500 +0.18033500 
+0.51700600 +0.31599200 +0.91484800 +0.46082500 +0.73198000 

+0.24335255 -2.67946180 +3.57538817 +0.83711880 +0.34704217 
+1.02790497 -1.05086895 -0.07468137 +0.71041070 +0.66708313 
-0.21087237 -4.47765165 +1.73958308 +1.73999641 +3.69324020 
-0.14100897 +2.34977565 -0.93725915 +0.47383541 -2.15554470 
-0.26329660 +6.46315378 -4.07721533 -3.37094863 -2.42580445