C ++에서 LAPACK을 사용하는 방법은 무엇입니까?


10

저는 컴퓨터 과학에 익숙하지 않고 이미 C ++에서 RK4, Numerov 등과 같은 통합, 보간법, 방법에 대한 기본 방법을 배웠지 만 최근 교수님은 매트릭스와 관련된 문제를 해결하기 위해 LAPACK을 사용하는 방법을 배우도록 요청했습니다. 예를 들어 복소수 행렬의 고유 값을 찾는 것과 같습니다. 나는 타사 라이브러리를 사용한 적이 없으며 거의 ​​항상 내 자신의 함수를 작성합니다. 나는 며칠 동안 주변을 검색했지만 lapack에 대한 아마추어 친화적 인 가이드를 찾을 수 없습니다. 그들 모두는 이해할 수없는 단어로 작성되었으며 이미 작성된 기능을 사용하는 것이 왜 그렇게 복잡 해야하는지 모르겠습니다. 그들은 zgeev, dtrsv 등과 같은 단어들로 가득차 있으며 좌절합니다. 이 의사 코드와 같은 코드를 작성하고 싶습니다.

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

내가 바보인지 아마추어인지 모르겠습니다. 그러나 다시, 이것은 그렇게 어렵지 않아야합니까? LAPACK 또는 LAPACK ++을 사용 해야하는지조차 알지 못합니다. (C ++로 코드를 작성하고 Python 또는 FORTRAN에 대한 지식이 없음) 및 설치 방법.


아마도이 예제가 유용 할 것입니다 : matrixprogramming.com/files/code/LAPACK
nukeguy

방금 시작한 경우 ArrayFire github.com/arrayfire/arrayfire 와 같이 더 간단한 라이브러리를 사용하는 것이 더 쉬울 입니다. C ++에서 직접 호출 할 수 있으며 API가 더 간단하며 LAPACK이 수행하는 모든 작업을 수행 할 수 있다고 생각합니다.
Vikram

이 다른 게시물 에서 사용자는 자신의 래퍼 FLENS를 제안하는데, LAPACK을 쉽게 소개 할 수있는 매우 좋은 구문이 있습니다.
Zythos

LAPACK 함수를 직접 호출하는 것은 매우 지루하고 오류가 발생하기 쉽습니다. LAPACK 용으로 사용하기 편리한 C ++ 래퍼가 몇 가지 있는데, Armadillo 와 같이 훨씬 더 사용하기 쉽습니다 . 복잡한 고유 분해의 특정 사용 사례에 대해서는 이 LAPACK 괴물 인 zheev (JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INFO)를 감싸는 사용자 친화적 인 eig_gen () 함수를 참조하십시오. 획득 된 고유 값 및 고유 벡터를 표준 표현으로 재 포맷한다.
hbrerkere

답변:


18

나는 다른 답변들 중 일부에 동의하지 않고 과학 컴퓨팅 분야에서 LAPACK을 사용하는 방법을 알아내는 것이 중요 하다고 생각합니다 .

그러나 LAPACK 사용에는 큰 학습 곡선이 있습니다. 매우 낮은 수준으로 작성 되었기 때문입니다. 단점은 매우 비밀스럽고 감각이 즐겁지 않다는 것입니다. 그것의 장점은 인터페이스가 명확하고 기본적으로 변경되지 않는다는 것입니다. 또한 Intel Math Kernel Library 와 같은 LAPACK의 구현 은 실제로 빠릅니다.

내 자신의 목적을 위해 LAPACK 서브 루틴을 둘러싼 고급 C ++ 클래스가 있습니다. 많은 과학 도서관에서도 LAPACK을 사용합니다. 때로는 그냥 사용하는 것이 더 쉽지만 내 생각으로는 도구를 이해하는 데 많은 가치가 있습니다. 이를 위해 LAPACK을 사용하여 C ++로 작성된 작은 작업 예제를 제공했습니다. 우분투에서 liblapack3패키지가 설치되어 있고 빌드에 필요한 다른 패키지가 있습니다. 아마도 대부분의 Linux 배포판에서 사용될 수 있지만 LAPACK 설치 및 링크는 다를 수 있습니다.

여기 파일이 있습니다 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;
}

이것은 커맨드 라인을 사용하여 만들 수 있습니다

g++ -o test_lapack test_lapack.cpp -llapack

라는 이름의 실행 파일이 생성됩니다 test_lapack. 텍스트 입력 파일을 읽도록 설정했습니다. 다음 matrix.txt은 3x3 행렬을 포함 하는 파일 입니다.

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

프로그램을 실행하려면 다음을 입력하십시오.

./test_lapack matrix.txt

명령 행에서 출력은

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

코멘트:

  • LAPACK의 이름 지정 체계에서 벗어난 것 같습니다. 간단한 설명은 여기에 있습니다 .
  • DGEEV 서브 루틴의 인터페이스는 다음과 같습니다 . 여기서 논증의 설명을 내가 여기서 한 것과 비교할 수 있어야합니다.
  • extern "C"상단 의 섹션을 확인하고에 밑줄을 추가했습니다 dgeev_. 라이브러리가 Fortran에서 작성되고 빌드 되었기 때문에 링크 할 때 기호가 일치하도록해야합니다. 이것은 컴파일러 및 시스템에 따라 다르므로 Windows에서 이것을 사용하는 경우 모두 변경해야합니다.
  • 일부 사람들은 LAPACK에 C 인터페이스를 사용하도록 제안 할 수 있습니다 . 그들이 옳을 수도 있지만, 나는 항상 이런 식으로 해왔다.

3
당신이 찾고있는 많은 것들이 빠른 Googlage로 찾을 수 있습니다. 무엇을 검색해야할지 모를 수도 있습니다. Netlib은 LAPACK의 골키퍼입니다. 설명서는 여기 에서 찾을 수 있습니다 . 이 페이지 에는 LAPACK의 주요 기능에 대한 유용한 표가 있습니다. 중요한 것 중 일부는 (1) 방정식 풀기 시스템, (2) 고유 값 문제, (3) 특이 값 분해 및 (4) QR 인수 분해입니다. DGEEV의 설명서를 이해 했습니까?
LedHead

1
그것들은 모두 같은 것에 대한 다른 인터페이스입니다. LAPACK은 원본입니다. Fortran으로 작성되었으므로 사용하려면 C / C ++에서 크로스 컴파일을 수행하기 위해 게임을해야합니다. 나는 LAPACKE을 사용한 적이 없지만 LAPACK에 비해 상당히 얇은 C 래퍼 인 것처럼 보이 므로이 크로스 컴파일 비즈니스를 피할 수는 있지만 여전히 저수준입니다. LAPACK ++은 훨씬 높은 수준의 C ++ 래퍼 인 것처럼 보이지만 더 이상 지원되지 않는다고 생각합니다 (잘못되면 누군가가 나를 수정합니다).
LedHead

1
특정 코드 모음을 모릅니다. 그러나 LAPACK 서브 루틴 이름을 Google에 올리면 StackExchange 사이트 중 하나에서 항상 오래된 질문을 찾을 수 있습니다.
LedHead

1
@AlirezaHashemi 그런데 WORK 어레이를 제공해야하는 이유는 일반적으로 LAPACK이 서브 루틴 내에 메모리를 할당하지 않기 때문입니다. LAPACK을 사용하는 경우 메모리 덩어리를 사용하고 메모리 할당 비용이 비싸므로 호출 루틴이 메모리 할당을 담당하게하는 것이 합리적입니다. DGEEV는 중간 수량을 저장하기 위해 메모리가 필요하므로 작업 공간을 제공해야합니다.
LedHead

1
알았다. 그리고 zgeev를 사용하여 복잡한 행렬의 고유 값을 계산하는 첫 번째 코드를 성공적으로 작성했습니다. 그리고 이미 더 많은 일을하고 있습니다! 감사!
Alireza

7

나는 보통 사람들에게 그들의 질문에 대답하기보다는 내가해야한다고 생각하는 것을 말하는 것을 거부하지만이 경우에는 예외를 만들 것이다.

Lapack은 FORTRAN으로 작성되었으며 API는 매우 FORTRAN과 유사합니다. Lapack에는 C API가있어서 인터페이스가 약간 덜 고통 스럽지만 C ++에서 Lapack을 사용하는 것은 결코 즐거운 경험이 될 수 없습니다.

또는 Laipack의 많은 기능을 가지고 있고 더 나은 Lapack 구현과 비슷한 계산 성능을 제공하며 C ++에서 사용하기 매우 편리한 Eigen 이라는 C ++ 매트릭스 클래스 라이브러리 가 있습니다. 특히, 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;
}

이 고유 값 문제 예는 Lapack 함수의 테스트 사례입니다 dgeev. 이 문제점 dgeev 예에 대한 FORTRAN 코드 및 결과를보고 직접 비교할 수 있습니다.


답변과 설명 감사합니다! 이 라이브러리를 시험 해보고 내 필요에 가장 적합한 것을 선택합니다.
Alireza

아, 그들은 과부하 operator,! 실제 연습에서 본 적이 없다 :-)
Wolfgang Bangerth

1
실제로, operator,과부하는 처음 나타날 때보 다 더 흥미롭고 더 좋습니다. 행렬을 초기화하는 데 사용됩니다. 행렬을 초기화하는 항목은 스칼라 상수 일 수 있지만 이전에 정의 된 행렬 또는 하위 행렬 일 수도 있습니다. 매우 MATLAB과 같습니다. 내 C ++ 프로그래밍 능력이 나 자신에게 세련된 것을 구현하기에 충분했으면 좋겠다. ;-)
Bill Greene

7

위와 같은 맥락에서 또 다른 대답이 있습니다.

Armadillo C ++ 선형 대수 라이브러리를 살펴 봐야 합니다 .

장점 :

  1. 함수 구문은 고급 수준입니다 (MATLAB과 유사). 따라서 DGESV점보 점보는 없습니다 X = solve( A, B )( 이상하게 보이는 LAPACK 함수 이름 뒤에 이유 가 있지만 ...).
  2. 다양한 매트릭스 분해 (LU, QR, 고유 값, SVD, Cholesky 등)를 구현합니다.
  3. 그것은이다 빠른 적절하게 사용하는 경우.
  4. 문서화되어 있습니다.
  5. 희소 행렬을 지원합니다 (나중에 살펴볼 것임).
  6. 최적의 성능을 위해 최적화 된 BLAS / LAPACK 라이브러리와 링크 할 수 있습니다.

다음은 @BillGreene의 코드가 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;
}

답변과 설명 감사합니다! 이 라이브러리를 시험 해보고 내 필요에 가장 적합한 것을 선택합니다.
Alireza
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.