Làm thế nào để bắt đầu sử dụng LAPACK trong c ++?


10

Tôi chưa quen với khoa học tính toán và tôi đã học các phương pháp cơ bản để tích hợp, nội suy, các phương pháp như RK4, Numerov, v.v. Ví dụ như tìm giá trị riêng của một ma trận phức tạp. Tôi chưa bao giờ sử dụng các thư viện của bên thứ ba và tôi hầu như luôn viết các chức năng của riêng mình. Tôi đã tìm kiếm xung quanh trong vài ngày nhưng không thể tìm thấy bất kỳ hướng dẫn thân thiện với nghiệp dư nào về lapack. Tất cả chúng đều được viết bằng những từ mà tôi không hiểu và tôi không biết tại sao việc sử dụng các hàm đã được viết lại lại phức tạp đến vậy. Chúng có đầy đủ các từ như zgeev, dtrsv, v.v. và tôi thất vọng. Tôi chỉ muốn mã một cái gì đó giống như mã giả này:

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

Tôi không biết mình đang ngớ ngẩn hay nghiệp dư. Nhưng một lần nữa, điều này không nên khó khăn như vậy? Tôi thậm chí không biết tôi nên sử dụng LAPACK hoặc LAPACK ++. (Tôi viết mã bằng c ++ và không có kiến ​​thức về Python hoặc FORTRAN) và cách cài đặt chúng.


Có lẽ ví dụ này sẽ hữu ích: matrixprogramming.com/files/code/LAPACK
nukeguy

Nếu bạn chỉ mới bắt đầu, có thể việc sử dụng một thư viện đơn giản hơn như ArrayFire github.com/arrayfire/arrayfire sẽ dễ dàng hơn . Bạn có thể gọi nó trực tiếp từ C ++ và API đơn giản hơn và tôi nghĩ nó có thể thực hiện tất cả các hoạt động mà LAPACK thực hiện.
Vikram

Trong bài đăng khác này, một người dùng đề xuất FLENS trình bao bọc của riêng mình, có một cú pháp rất hay có thể giúp bạn dễ dàng giới thiệu về LAPACK.
Zythos

Gọi các chức năng LAPACK trực tiếp là rất tẻ nhạt và dễ bị lỗi. Có một số trình bao bọc C ++ thân thiện với người dùng cho LAPACK, cung cấp việc sử dụng dễ dàng hơn nhiều, chẳng hạn như Armadillo . Đối với trường hợp sử dụng cụ thể của phân tách eigen phức tạp, hãy xem hàm eig_gen () thân thiện với người dùng , bên dưới bao bọc sự quái dị LAPACK này, zheev (JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INWOR, và định dạng lại các giá trị riêng và hàm riêng cho các biểu diễn chuẩn.
hbrerkere

Câu trả lời:


18

Tôi sẽ không đồng ý với một số câu trả lời khác và nói rằng tôi tin rằng việc tìm ra cách sử dụng LAPACK rất quan trọng trong lĩnh vực điện toán khoa học.

Tuy nhiên, có một đường cong học tập lớn để sử dụng LAPACK. Điều này là do nó được viết ở mức rất thấp. Nhược điểm của nó là có vẻ rất khó hiểu, và không dễ chịu với các giác quan. Ưu điểm của nó là giao diện không rõ ràng và về cơ bản không bao giờ thay đổi. Ngoài ra, việc triển khai LAPACK, chẳng hạn như Thư viện Hạt nhân Toán học Intel thực sự nhanh chóng.

Với mục đích riêng của mình, tôi có các lớp C ++ cấp cao hơn của riêng mình, bao quanh các chương trình con LAPACK. Nhiều thư viện khoa học cũng sử dụng LAPACK bên dưới. Đôi khi việc sử dụng chúng dễ dàng hơn, nhưng theo tôi có rất nhiều giá trị trong việc tìm hiểu công cụ bên dưới. Cuối cùng, tôi đã cung cấp một ví dụ hoạt động nhỏ được viết bằng C ++ bằng LAPACK để giúp bạn bắt đầu. Điều này hoạt động trong Ubuntu, với liblapack3gói được cài đặt và các gói cần thiết khác để xây dựng. Nó có thể được sử dụng trong hầu hết các bản phân phối Linux, nhưng cài đặt LAPACK và liên kết với nó có thể khác nhau.

Đây là tập tin 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;
}

Điều này có thể được xây dựng bằng cách sử dụng dòng lệnh

g++ -o test_lapack test_lapack.cpp -llapack

Điều này sẽ tạo ra một thực thi có tên test_lapack. Tôi đã thiết lập điều này để đọc trong tệp nhập văn bản. Đây là một tệp có tên matrix.txtchứa ma trận 3x3.

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

Để chạy chương trình, chỉ cần gõ

./test_lapack matrix.txt

tại dòng lệnh, và đầu ra phải là

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

Bình luận:

  • Bạn dường như bị loại bỏ bởi sơ đồ đặt tên cho LAPACK. Một mô tả ngắn ở đây .
  • Giao diện cho chương trình con DGEEV có ở đây . Bạn sẽ có thể so sánh mô tả của các đối số ở đó với những gì tôi đã làm ở đây.
  • Lưu ý extern "C"phần ở trên cùng và tôi đã thêm dấu gạch dưới vào dgeev_. Đó là bởi vì thư viện được viết và xây dựng ở Fortran, vì vậy điều này là cần thiết để làm cho các biểu tượng khớp với nhau khi liên kết. Đây là trình biên dịch và hệ thống phụ thuộc, vì vậy nếu bạn sử dụng điều này trên Windows, tất cả sẽ phải thay đổi.
  • Một số người có thể đề xuất sử dụng giao diện C để LAPACK . Họ có thể đúng, nhưng tôi luôn làm theo cách này.

3
Rất nhiều thứ bạn đang tìm kiếm có thể được tìm thấy với một số nhân viên nhanh chóng. Có lẽ bạn chỉ không chắc chắn những gì để tìm kiếm. Netlib là người giữ LAPACK. Các tài liệu có thể được tìm thấy ở đây . Trang này có một bảng tiện dụng về chức năng chính của LAPACK. Một số trong những vấn đề quan trọng là (1) hệ thống giải phương trình, (2) bài toán giá trị riêng, (3) phân tách giá trị số ít và (4) hệ số QR. Bạn đã hiểu hướng dẫn sử dụng cho DGEEV?
LedHead

1
Tất cả chúng chỉ là các giao diện khác nhau cho cùng một thứ. LAPACK là bản gốc. Nó được viết bằng Fortran, vì vậy để sử dụng nó, bạn phải chơi một số trò chơi để biên dịch chéo từ C / C ++ hoạt động, như tôi đã trình bày. Tôi chưa bao giờ sử dụng LAPACKE, nhưng có vẻ như nó là một trình bao bọc C khá mỏng so với LAPACK để tránh công việc biên dịch chéo này, nhưng nó vẫn ở mức độ khá thấp. LAPACK ++ dường như là một trình bao bọc C ++ cấp độ cao hơn, nhưng tôi không nghĩ nó còn được hỗ trợ nữa (ai đó sửa tôi nếu tôi sai).
LedHead

1
Tôi không biết về bất kỳ bộ sưu tập mã cụ thể nào. Nhưng nếu bạn Google bất kỳ tên chương trình con LAPACK nào, bạn sẽ luôn tìm thấy một câu hỏi cũ trên một trong các trang web StackExchange.
LedHead

1
@AlirezaHashemi Nhân tiện, lý do bạn phải cung cấp mảng WORK là vì theo quy tắc LAPACK không phân bổ bất kỳ bộ nhớ nào trong các chương trình con của nó. Nếu chúng tôi đang sử dụng LAPACK, có khả năng chúng tôi đang sử dụng bộ nhớ và phân bổ bộ nhớ rất tốn kém, do đó, sẽ hợp lý khi để các thói quen gọi điện chịu trách nhiệm phân bổ bộ nhớ. Vì DGEEV yêu cầu bộ nhớ để lưu trữ số lượng trung gian, chúng tôi phải cung cấp không gian làm việc cho nó.
LedHead

1
Hiểu rồi. Và tôi đã viết thành công mã đầu tiên của mình để tính giá trị riêng của một ma trận phức bằng zgeev. Và đã làm nhiều hơn nữa! Cảm ơn!
Alireza

7

Tôi thường chống lại việc nói với mọi người những gì tôi nghĩ họ nên làm hơn là trả lời câu hỏi của họ nhưng trong trường hợp này tôi sẽ tạo ra một ngoại lệ.

Lapack được viết bằng FORTRAN và API rất giống FORTRAN. Có một API C để Lapack làm cho giao diện bớt đau hơn một chút nhưng sẽ không bao giờ là một trải nghiệm thú vị khi sử dụng Lapack từ C ++.

Ngoài ra, có một thư viện lớp ma trận C ++ có tên Eigen có nhiều khả năng của Lapack, cung cấp hiệu năng tính toán tương đương với các triển khai Lapack tốt hơn và rất thuận tiện để sử dụng từ C ++. Cụ thể, đây là cách mã ví dụ của bạn có thể được viết bằng 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;
}

Ví dụ này vấn đề eigenvalue là một trường hợp thử nghiệm cho hàm Lapack dgeev. Bạn có thể xem mã FORTRAN và kết quả cho ví dụ về vấn đề này và đưa ra so sánh của riêng bạn.


Cảm ơn bạn đã trả lời và giải thích của bạn! Tôi sẽ thử thư viện này và chọn một trong những phù hợp với nhu cầu của tôi tốt nhất.
Alireza

Ồ, họ quá tải operator,! Chưa bao giờ thấy điều đó được thực hiện trong thực tế :-)
Wolfgang Bangerth

1
Trên thực tế, sự operator,quá tải đó thú vị hơn / tốt hơn so với lần đầu tiên xuất hiện. Nó được sử dụng để khởi tạo ma trận. Các mục khởi tạo ma trận có thể là hằng số vô hướng nhưng cũng có thể là ma trận được xác định trước hoặc ma trận phụ. Rất giống MATLAB. Chúc khả năng lập trình C ++ của tôi đủ tốt để thực hiện điều gì đó tinh vi cho bản thân ;-)
Bill Greene

7

Đây là một câu trả lời khác trong cùng một hướng dẫn như trên.

Bạn nên xem xét thư viện đại số tuyến tính Armadillo C ++ .

Ưu điểm:

  1. Cú pháp hàm là mức cao (tương tự như MATLAB). Vì vậy, không có DGESVmumbo-jumbo, chỉ X = solve( A, B )(mặc dù có một lý do đằng sau những tên hàm LAPACK trông kỳ quặc đó ...).
  2. Triển khai các phân tách ma trận khác nhau (LU, QR, giá trị riêng, SVD, Cholesky, v.v.)
  3. nhanh khi được sử dụng đúng cách.
  4. Nó là tài liệu tốt .
  5. Có hỗ trợ cho ma trận thưa thớt (bạn sẽ muốn xem xét những điều này sau).
  6. Bạn có thể liên kết nó với các thư viện BLAS / LAPACK được tối ưu hóa của bạn để có hiệu suất tối ưu.

Đây là cách mã của @ BillGreene trông như thế nào với 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;
}

Cảm ơn bạn đã trả lời và giải thích của bạn! Tôi sẽ thử thư viện này và chọn một trong những phù hợp với nhu cầu của tôi tốt nhất.
Alireza
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.