Computing the inverse of a matrix using lapack in C

前端 未结 4 1650
旧巷少年郎
旧巷少年郎 2020-12-07 19:24

I would like to be able to compute the inverse of a general NxN matrix in C/C++ using lapack.

My understanding is that the way to do an inversion in la

4条回答
  •  北荒
    北荒 (楼主)
    2020-12-07 19:48

    Here is a working version of Spencer Nelson's example above. One mystery about it is that the input matrix is in row-major order, even though it appears to call the underlying fortran routine dgetri. I am led to believe that all the underlying fortran routines require column-major order, but I am no expert on LAPACK, in fact, I'm using this example to help me learn it. But, that one mystery aside:

    The input matrix in the example is singular. LAPACK tries to tell you that by returning a 3 in the errorHandler. I changed the 9 in that matrix to a 19, getting an errorHandler of 0 signalling success, and compared the result to that from Mathematica. The comparison was also successful and confirmed that the matrix in the example should be in row-major order, as presented.

    Here is the working code:

    #include 
    #include 
    #include 
    
    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;   }
    

    I built and ran it as follows on a Mac:

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

    I did an nm on the LAPACKE library and found the following:

    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
    

    and it looks like there is a LAPACKE [sic] wrapper that would presumably relieve us of having to take addresses everywhere for fortran's convenience, but I am probably not going to get around to trying it because I have a way forward.

    EDIT

    Here is a working version that bypasses LAPACKE [sic], using LAPACK fortran routines directly. I do not understand why a row-major input produces correct results, but I confirmed it again in Mathematica.

    #include 
    #include 
    
    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;   }
    

    built and run like this:

    $ 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
    

    EDIT

    The mystery no longer appears to be a mystery. I think the computations are being done in column-major order, as they must, but I am both inputting and printing the matrices as if they were in row-major order. I have two bugs that cancel each other out so things look row-ish even though they're column-ish.

提交回复
热议问题