2010-08-19 158 views
18

Tôi muốn có thể tính toán nghịch đảo của ma trận NxN chung trong C/C++ bằng cách sử dụng lapack.Tính toán nghịch đảo của ma trận sử dụng lapack trong C

Sự hiểu biết của tôi là cách làm đảo ngược trong lapack là sử dụng hàm dgetri, tuy nhiên, tôi không thể tìm ra tất cả các đối số của nó được cho là như thế nào.

Dưới đây là đoạn code tôi có:

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

Làm thế nào bạn sẽ hoàn thành nó để có được những nghịch đảo của ma trận M 3x3 sử dụng dgetri_?

Trả lời

16

Trước tiên, M phải là mảng hai chiều, như double M[3][3]. Mảng của bạn, nói một cách toán học, một vector 1x9, không thể đảo ngược được.

  • N là một con trỏ đến một int cho trật tự của ma trận - trong trường hợp này, N = 3.

  • A là một con trỏ đến LU nhân tử của ma trận, mà bạn có thể nhận được bằng cách chạy LAPACK thói quen dgetrf.

  • LDA là một số nguyên cho "hàng đầu yếu tố" của ma trận, cho phép bạn chọn ra một tập hợp con của một ma trận lớn hơn nếu bạn muốn chỉ cần đảo ngược một chút mảnh . Nếu bạn muốn đảo ngược toàn bộ ma trận, LDA nên chỉ được bằng N.

  • IPIV là các chỉ số trục của ma trận , nói cách khác, đó là một danh sách các hướng dẫn về những gì hàng để trao đổi để đảo ngược ma trận. IPIV phải được tạo bởi LAPACK thường lệ dgetrf.

  • LWORK và WORK là "không gian làm việc" được LAPACK sử dụng. Nếu bạn đang đảo ngược toàn bộ ma trận, LWORK phải là int bằng N^2 và WORK phải là một mảng kép với các phần tử LWORK.

  • INFO chỉ là biến trạng thái để cho bạn biết liệu hoạt động đã hoàn tất thành công hay chưa. Vì không phải tất cả các ma trận đều không thể đảo ngược, tôi sẽ khuyên bạn nên gửi số này tới một số hệ thống kiểm tra lỗi . INFO = 0 cho hoạt động thành công, INFO = -i nếu đối số thứ i có giá trị đầu vào không chính xác và INFO> 0 nếu ma trận không thể đảo ngược.

Vì vậy, đối với mã của bạn, tôi sẽ làm một cái gì đó như thế này:

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

Về 1x9 hoặc 3x3. Có thực sự không có bất kỳ sự khác biệt về bố trí bộ nhớ. Trong thực tế, các thói quen BLAS/LAPACK không nhận mảng 2d. Họ lấy 1d mảng và đưa ra giả định về cách bạn đã đặt nó ra. Hãy coi chừng mặc dù BLAS và LAPACK sẽ giả định thứ tự FORTRAN (hàng thay đổi nhanh nhất) thay vì đặt hàng C. – MRocklin

+0

Bạn có thể cần 'LAPACKE_dgetrf' thay vì thường trình fortran' dgetrf_'. Ditto 'dgetri'. Khác bạn phải gọi tất cả mọi thứ với địa chỉ. –

19

Dưới đây là đoạn code làm việc để tính nghịch đảo của một ma trận sử dụng LAPACK trong C/C++:

#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

Bạn không cần phân bổ N + 1 (nhưng chỉ N unsigned) int cho biến IPIV. Hơn nữa, tôi không khuyên bạn nên sử dụng loại chức năng này để tính toán nghịch đảo bội số. Phân bổ dữ liệu bạn cần một lần cho tất cả và miễn phí ở cuối chỉ. – matovitch

+0

Bạn có thể cần 'delete [] IPIV' và' delete [] work'. –

+1

Không có ngôn ngữ C/C++. Mã bạn hiển thị là C++. Nhưng câu hỏi là về C. – Olaf

0

Đây là phiên bản hoạt động của ví dụ của Spencer Nelson ở trên. Một bí ẩn về nó là ma trận đầu vào nằm trong thứ tự hàng lớn, mặc dù nó xuất hiện để gọi thường trình fortran cơ bản là dgetri. Tôi tin rằng tất cả các thói quen fortran cơ bản đòi hỏi thứ tự cột lớn, nhưng tôi không có chuyên gia về LAPACK, trên thực tế, tôi đang sử dụng ví dụ này để giúp tôi tìm hiểu nó. Nhưng, một điều bí ẩn đó sang một bên:

Ma trận đầu vào trong ví dụ này là số ít. LAPACK cố gắng cho bạn biết rằng bằng cách trả lại 3 trong số errorHandler. Tôi đã thay đổi 9 trong ma trận đó thành một số 19, nhận được một số errorHandler của 0 báo hiệu thành công và so sánh kết quả với điều đó từ Mathematica. So sánh cũng thành công và xác nhận rằng ma trận trong ví dụ phải theo thứ tự hàng lớn, như đã trình bày.

Dưới đây là đoạn code làm việc:

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

tôi đã xây dựng và chạy nó như sau trên máy Mac:

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

Tôi đã làm một nm trên thư viện LAPACKE và thấy như sau:

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 

và có vẻ như có một trình bao bọc LAPACKE [sic] có lẽ sẽ giảm bớt chúng tôi phải có địa chỉ ở khắp mọi nơi để thuận tiện cho fortran, nhưng tôi có lẽ sẽ không đi xung quanh để thử nó bởi vì tôi có một con đường phía trước.

EDIT

Đây là một phiên bản làm việc mà bỏ qua LAPACKE [sic], thói quen sử dụng fortran LAPACK trực tiếp. Tôi không hiểu tại sao một đầu vào hàng lớn tạo ra kết quả chính xác, nhưng tôi đã xác nhận nó một lần nữa trong 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; } 

xây dựng và chạy như thế này:

$ 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

Các bí ẩn không còn dường như là một điều bí ẩn. Tôi nghĩ rằng các tính toán đang được thực hiện theo thứ tự cột lớn, vì chúng phải, nhưng tôi vừa nhập và in các ma trận như thể chúng nằm trong thứ tự lớn. Tôi có hai lỗi mà hủy bỏ lẫn nhau để mọi thứ trông hàng-ish mặc dù họ đang cột-ish.

2

Đây là phiên bản hoạt động của giao diện OpenBlas ở trên sử dụng LAPACKE. Liên kết với thư viện openblas (LAPACKE đã được chứa)

#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'); 
} 

Ví dụ:

% 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