Come iniziare a usare LAPACK in c ++?


10

Sono nuovo nella scienza computazionale e ho già imparato i metodi di base per l'integrazione, l'interpolazione, metodi come RK4, Numerov ecc. Su c ++, ma recentemente il mio professore mi ha chiesto di imparare come usare LAPACK per risolvere i problemi relativi alle matrici. Come ad esempio la ricerca di autovalori di una matrice complessa. Non ho mai usato librerie di terze parti e scrivo quasi sempre le mie funzioni. Ho cercato in giro per diversi giorni, ma non riesco a trovare nessuna guida amatoriale per Lapack. Tutti sono scritti con parole che non capisco e non so perché usare funzioni già scritte dovrebbe essere così complicato. Sono pieni di parole come zgeev, dtrsv, ecc. E sono frustrato. Voglio solo codificare qualcosa come questo pseudo-codice:

#include <lapack:matrix>
int main(){
  LapackComplexMatrix A(n,n);
  for...
   for...
    cin>>A(i,j);
  cout<<LapackEigenValues(A);
  return 0;
}

Non so se sono sciocco o dilettante. Ma ancora una volta, non dovrebbe essere così difficile? Non so nemmeno se dovrei usare LAPACK o LAPACK ++. (Scrivo codici in c ++ e non ho conoscenza di Python o FORTRAN) e di come installarli.


Forse questo esempio sarebbe utile: matrixprogramming.com/files/code/LAPACK
nukeguy

Se hai appena iniziato, forse sarà più facile usare una libreria più semplice come ArrayFire github.com/arrayfire/arrayfire . Puoi chiamarlo direttamente da C ++ e le API sono più semplici e penso che possa fare tutte le operazioni che LAPACK fa.
Vikram,

In questo altro post un utente propone il proprio wrapper FLENS, che ha una sintassi molto piacevole che potrebbe facilitare la tua introduzione a LAPACK.
Zythos

Chiamare direttamente le funzioni LAPACK è molto noioso e soggetto a errori. Esistono diversi wrapper C ++ intuitivi per LAPACK che forniscono un utilizzo molto più semplice, come Armadillo . Per il caso d'uso specifico della decomposizione di eigen complessi, vedere la funzione eig_gen () intuitiva , che sotto avvolge questa mostruosità LAPACK, zheev (JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INFO), e riformatta gli autovalori e gli autovettori ottenuti in rappresentazioni standard.
hbrerkere,

Risposte:


18

Non sarò d'accordo con alcune delle altre risposte e dirò che credo che capire come usare LAPACK sia importante nel campo dell'informatica scientifica.

Tuttavia, esiste una grande curva di apprendimento nell'uso di LAPACK. Questo perché è scritto a un livello molto basso. Lo svantaggio di ciò è che sembra molto criptico e non piacevole ai sensi. Il vantaggio è che l'interfaccia è inequivocabile e sostanzialmente non cambia mai. Inoltre, le implementazioni di LAPACK, come Intel Math Kernel Library, sono davvero veloci.

Per i miei scopi, ho le mie classi C ++ di livello superiore che avvolgono le subroutine LAPACK. Molte biblioteche scientifiche usano anche LAPACK al di sotto. A volte è più semplice usarli, ma secondo me c'è molto valore nella comprensione dello strumento sottostante. A tal fine, ho fornito un piccolo esempio funzionante scritto in C ++ usando LAPACK per iniziare. Funziona in Ubuntu, con il liblapack3pacchetto installato e altri pacchetti necessari per la creazione. Probabilmente può essere usato nella maggior parte delle distribuzioni Linux, ma l'installazione di LAPACK e il suo collegamento possono variare.

Ecco il file test_lapack.cpp

#include <iostream>
#include <fstream>


using namespace std;

// dgeev_ is a symbol in the LAPACK library files
extern "C" {
extern int dgeev_(char*,char*,int*,double*,int*,double*, double*, double*, int*, double*, int*, double*, int*, int*);
}

int main(int argc, char** argv){

  // check for an argument
  if (argc<2){
    cout << "Usage: " << argv[0] << " " << " filename" << endl;
    return -1;
  }

  int n,m;
  double *data;

  // read in a text file that contains a real matrix stored in column major format
  // but read it into row major format
  ifstream fin(argv[1]);
  if (!fin.is_open()){
    cout << "Failed to open " << argv[1] << endl;
    return -1;
  }
  fin >> n >> m;  // n is the number of rows, m the number of columns
  data = new double[n*m];
  for (int i=0;i<n;i++){
    for (int j=0;j<m;j++){
      fin >> data[j*n+i];
    }
  }
  if (fin.fail() || fin.eof()){
    cout << "Error while reading " << argv[1] << endl;
    return -1;
  }
  fin.close();

  // check that matrix is square
  if (n != m){
    cout << "Matrix is not square" <<endl;
    return -1;
  }

  // allocate data
  char Nchar='N';
  double *eigReal=new double[n];
  double *eigImag=new double[n];
  double *vl,*vr;
  int one=1;
  int lwork=6*n;
  double *work=new double[lwork];
  int info;

  // calculate eigenvalues using the DGEEV subroutine
  dgeev_(&Nchar,&Nchar,&n,data,&n,eigReal,eigImag,
        vl,&one,vr,&one,
        work,&lwork,&info);


  // check for errors
  if (info!=0){
    cout << "Error: dgeev returned error code " << info << endl;
    return -1;
  }

  // output eigenvalues to stdout
  cout << "--- Eigenvalues ---" << endl;
  for (int i=0;i<n;i++){
    cout << "( " << eigReal[i] << " , " << eigImag[i] << " )\n";
  }
  cout << endl;

  // deallocate
  delete [] data;
  delete [] eigReal;
  delete [] eigImag;
  delete [] work;


  return 0;
}

Questo può essere creato usando la riga di comando

g++ -o test_lapack test_lapack.cpp -llapack

Questo produrrà un eseguibile chiamato test_lapack. Ho impostato questo per leggere in un file di input di testo. Ecco un file chiamato matrix.txtcontenente una matrice 3x3.

3 3
-1.0 -8.0  0.0
-1.0  1.0 -5.0
 3.0  0.0  2.0

Per eseguire il programma è sufficiente digitare

./test_lapack matrix.txt

alla riga di comando e l'output dovrebbe essere

--- Eigenvalues ---
( 6.15484 , 0 )
( -2.07742 , 3.50095 )
( -2.07742 , -3.50095 )

Commenti:

  • Sembri scagliato dallo schema di denominazione di LAPACK. Una breve descrizione è qui .
  • L'interfaccia per la subroutine DGEEV è qui . Dovresti essere in grado di confrontare la descrizione degli argomenti lì con quello che ho fatto qui.
  • Nota la extern "C"sezione in alto e alla quale ho aggiunto un trattino basso dgeev_. Questo perché la libreria è stata scritta e costruita in Fortran, quindi questo è necessario per far corrispondere i simboli durante il collegamento. Questo dipende dal compilatore e dal sistema, quindi se lo usi su Windows, tutto dovrà cambiare.
  • Alcune persone potrebbero suggerire di utilizzare l' interfaccia C per LAPACK . Potrebbero avere ragione, ma l'ho sempre fatto così.

3
Molto di ciò che stai cercando può essere trovato con alcuni googlage veloci. Forse non sei sicuro di cosa cercare. Netlib è il custode di LAPACK. La documentazione è disponibile qui . Questa pagina contiene una comoda tabella delle principali funzionalità di LAPACK. Alcuni di quelli importanti sono (1) risoluzione di sistemi di equazioni, (2) problemi di autovalori, (3) scomposizioni di valori singolari e (4) fattorizzazioni QR. Hai capito il manuale di DGEEV?
LedHead

1
Sono tutte interfacce diverse per la stessa cosa. LAPACK è l'originale. È scritto in Fortran, quindi per usarlo devi giocare ad alcuni giochi per far funzionare la compilazione incrociata da C / C ++, come ho mostrato. Non ho mai usato LAPACKE, ma sembra che sia un wrapper C piuttosto sottile rispetto a LAPACK che evita questo business di compilazione incrociata, ma è ancora piuttosto di basso livello. LAPACK ++ sembra essere un wrapper C ++ di livello ancora più alto, ma non credo sia nemmeno più supportato (qualcuno mi corregga se sbaglio).
LedHead

1
Non conosco alcuna raccolta di codice specifica. Ma se si utilizza uno dei nomi di subroutine LAPACK su Google, invariabilmente troverete una vecchia domanda su uno dei siti StackExchange.
LedHead

1
@AlirezaHashemi A proposito, il motivo per cui devi fornire l'array WORK è perché di norma LAPACK non alloca memoria all'interno delle sue subroutine. Se stiamo usando LAPACK, probabilmente stiamo usando gobs di memoria e l'allocazione della memoria è costosa, quindi ha senso lasciare che le routine di chiamata si occupino dell'allocazione della memoria. Poiché DGEEV richiede memoria per archiviare quantità intermedie, dobbiamo fornirvi quello spazio di lavoro.
LedHead

1
Fatto. E ho scritto con successo il mio primo codice per calcolare gli autovalori di una matrice complessa usando zgeev. E già facendo di più! Grazie!
Alireza,

7

Di solito resisto a dire alla gente cosa penso che dovrebbero fare piuttosto che rispondere alla loro domanda, ma in questo caso farò un'eccezione.

Lapack è scritto in FORTRAN e l'API è molto simile a FORTRAN. Esiste un'API C per Lapack che rende l'interfaccia leggermente meno dolorosa, ma non sarà mai un'esperienza piacevole usare Lapack da C ++.

In alternativa, esiste una libreria di classi di matrici C ++ chiamata Eigen che ha molte delle funzionalità di Lapack, fornisce prestazioni computazionali paragonabili alle migliori implementazioni di Lapack ed è molto conveniente da usare da C ++. In particolare, ecco come il tuo codice di esempio potrebbe essere scritto usando Eigen

#include <iostream>
using std::cout;
using std::endl;

#include <Eigen/Eigenvalues>

int main()
{
  const int n = 4;
  Eigen::MatrixXd a(n, n);
  a <<
    0.35, 0.45, -0.14, -0.17,
    0.09, 0.07, -0.54, 0.35,
    -0.44, -0.33, -0.03, 0.17,
    0.25, -0.32, -0.13, 0.11;
  Eigen::EigenSolver<Eigen::MatrixXd> es;
  es.compute(a);
  Eigen::VectorXcd ev = es.eigenvalues();
  cout << ev << endl;
}

Questo problema di autovalore di esempio è un caso di test per la funzione Lapack dgeev. È possibile visualizzare il codice FORTRAN e i risultati per questo esempio dgeev problema e fare i propri confronti.


Grazie per la tua risposta e spiegazione! Proverò questa libreria e sceglierò quella più adatta alle mie esigenze.
Alireza,

Oh, si sovraccaricano operator,! Mai visto ciò fatto nella pratica reale :-)
Wolfgang Bangerth

1
In realtà, quel operator,sovraccarico è più interessante / migliore di quanto potrebbe apparire per la prima volta. Viene utilizzato per inizializzare le matrici. Le voci che inizializzano la matrice possono essere costanti scalari ma possono anche essere matrici o sotto-matrici precedentemente definite. Molto simile a MATLAB. Vorrei che la mia capacità di programmazione in C ++ fosse abbastanza buona da implementare qualcosa di così sofisticato ;-)
Bill Greene,

7

Ecco un'altra risposta nella stessa vena di cui sopra.

Dovresti esaminare la libreria di algebra lineare Armadillo C ++ .

Professionisti:

  1. La sintassi della funzione è di alto livello (simile a quella di MATLAB). Quindi niente DGESVmumbo-jumbo, solo X = solve( A, B )(anche se c'è un motivo dietro quei nomi di funzioni LAPACK dall'aspetto strano ...).
  2. Implementa varie scomposizioni di matrici (LU, QR, autovalori, SVD, Cholesky, ecc.)
  3. È veloce se usato correttamente.
  4. È ben documentato .
  5. Ha il supporto per matrici sparse (vorrai esaminarle più avanti).
  6. Puoi collegarlo alle tue librerie BLAS / LAPACK super ottimizzate per prestazioni ottimali.

Ecco come sarebbe il codice di @ BillGreene con Armadillo:

#include <iostream>
#include <armadillo>

using namespace std;
using namespace arma;

int main()
{
   const int k = 4;
   mat A = zeros<mat>(k,k) // mat == Mat<double>

   // with the << operator...
   A <<
    0.35 << 0.45 << -0.14 << -0.17 << endr
    0.09 << 0.07 << -0.54 << 0.35  << endr
    -0.44 << -0.33 << -0.03 << 0.17 << endr
    0.25 << -0.32 << -0.13 << 0.11 << endr;

   // but using an initializer list is faster
   A = { {0.35, 0.45, -0.14, -0.17}, 
         {0.09, 0.07, -0.54, 0.35}, 
         {-0.44, -0.33, -0.03, 0.17}, 
         {0.25, -0.32, -0.13, 0.11} };

   cx_vec eigval; // eigenvalues may well be complex
   cx_mat eigvec;

   // eigenvalue decomposition for general dense matrices
   eig_gen(eigval, eigvec, A);

   std::cout << eigval << std::endl;

   return 0;
}

Grazie per la tua risposta e spiegazione! Proverò questa libreria e sceglierò quella più adatta alle mie esigenze.
Alireza,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.