2010-08-19 14 views
18

Vorrei poter calcolare l'inverso di una matrice generale NxN in C/C++ utilizzando lapack.Calcolo dell'inverso di una matrice utilizzando il pacchetto in C

La mia comprensione è che il modo di fare un'inversione in lapack è usando la funzione dgetri, tuttavia, non riesco a capire quali dovrebbero essere tutti i suoi argomenti.

Ecco il codice che ho:

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

Come ti completarlo per ottenere l'inverso della matrice M 3x3 utilizzando dgetri_?

risposta

16

In primo luogo, M deve essere una matrice bidimensionale, ad esempio double M[3][3]. Il tuo array è, matematicamente parlando, un vettore 1x9, che non è invertibile.

  • N è un puntatore ad un int per la ordine della matrice - in questo caso, N = 3.

  • A è un puntatore al LU fattorizzazione della matrice, che è possibile ottenere eseguendo il LAPACK di routine dgetrf.

  • LDA è un numero intero per il "leader elemento" della matrice, che consente a scegliere un sottoinsieme di una matrice più grande se si vuole invertire solo un piccolo pezzo . Se si vuole invertire l'intera matrice, LDA dovrebbe essere solo uguale a N.

  • IPIV è gli indici di rotazione della matrice , in altre parole, si tratta di una lista delle istruzioni di ciò righe da scambiare per invertire la matrice. IPIV deve essere generato dalla routine LAPACK dgetrf.

  • LWORK e WORK sono le "aree di lavoro" utilizzate da LAPACK. Se si sta invertendo l'intera matrice, LWORK deve essere un int uguale a N^2 e WORK deve essere un array doppio con elementi LWORK.

  • INFO è solo una variabile di stato a indica se l'operazione è stata completata correttamente. Dal momento che non tutte le matrici sono invertibili, mi piacerebbe che l' raccomandi di inviarlo a qualche tipo di sistema di controllo degli errori . INFO = 0 per operazione riuscita, INFO = -i se l'argomento ith aveva un valore di input errato e INFO> 0 se la matrice non è invertibile.

Quindi, per il codice, vorrei fare qualcosa di simile:

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

Per quanto riguarda 1x9 o 3x3. Non c'è davvero alcuna differenza in termini di layout di memoria. In effetti le routine BLAS/LAPACK non utilizzano array 2D. Prendono 1d array e fanno ipotesi su come lo hai esposto. Attenzione però che BLAS e LAPACK assumeranno l'ordine FORTRAN (le righe cambiano più velocemente) anziché l'ordinamento C. – MRocklin

+0

Potrebbe essere necessario 'LAPACKE_dgetrf' piuttosto che la routine fortran' dgetrf_'. Idem 'dgetri'. Altrimenti devi chiamare tutto con gli indirizzi. –

19

Ecco il codice di lavoro per calcolare l'inversa di una matrice utilizzando LAPACK in 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

Non è necessario assegnare N + 1 (ma solo N senza segno) int per la variabile IPIV. Inoltre, non consiglio di usare questo tipo di funzione per calcolare i multipli inversi. Assegna i dati di cui hai bisogno una volta per tutte e gratuitamente solo alla fine. – matovitch

+0

Potresti aver bisogno di 'delete [] IPIV' e' delete [] work'. –

+1

Non esiste una lingua C/C++. Il codice che mostri è C++. Ma la domanda è su C. – Olaf

0

Ecco una versione funzionante dell'esempio di Spencer Nelson sopra. Un mistero è che la matrice di input è in ordine di riga principale, anche se sembra chiamare la routine di fortran sottostante dgetri. Sono portato a credere che tutte le routine di fortran sottostanti richiedano un ordine di colonna principale, ma non sono un esperto di LAPACK, infatti, sto usando questo esempio per aiutarmi ad apprenderlo. Ma, a parte questo mistero:

La matrice di input nell'esempio è singolare. LAPACK prova a dirti che restituendo uno 3 nel errorHandler. Ho modificato il 9 in quella matrice in un 19, ottenendo un errorHandler di 0 segnalazione riuscita e confrontato il risultato con quello da Mathematica. Anche il confronto ha avuto esito positivo e ha confermato che la matrice nell'esempio dovrebbe essere in ordine di riga maggiore, come presentato.

Ecco il codice di lavoro:

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

ho costruito e fatto funzionare come segue su un Mac:

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

ho fatto un nm sulla libreria LAPACKE e trovato il seguente:

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 

e sembra che ci sia un wrapper LAPACKE [sic] che presumibilmente allevierà noi di dover prendere indirizzi ovunque per comodità di Fortran, ma probabilmente non ho intenzione di andare in giro a provarlo perché ho una strada da percorrere.

EDIT

Ecco una versione di lavoro che bypassa LAPACKE [sic], utilizzando direttamente le routine FORTRAN LAPACK. Non capisco perché un input di riga principale produca risultati corretti, ma l'ho confermato di nuovo in 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; } 

costruite e gestite in questo modo:

$ 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

Il mistero non è più sembra essere un mistero. Penso che i calcoli vengano eseguiti in ordine di colonna principale, come devono, ma sto sia inserendo e stampando le matrici come se fossero in ordine di riga principale. Ho due bug che si annullano a vicenda, quindi le cose sembrano in fila, anche se sono colonne-ish.

2

Ecco una versione funzionante di quanto sopra utilizzando l'interfaccia OpenBlas a LAPACKE. Collegamento con biblioteca openblas (LAPACKE è già contenuta)

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

Esempio:

% 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 
Problemi correlati