Як почати використовувати LAPACK в c ++?


10

Я новачок в обчислювальній науці, і я вже вивчив основні методи інтеграції, інтерполяції, такі методи, як RK4, Numerov тощо, на c ++, але нещодавно мій професор попросив мене навчитися використовувати 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.
Вікрам

У цьому іншому дописі користувач пропонує власну обгортку FLENS, яка має дуже приємний синтаксис, який може полегшити ваше введення до програми LAPACK.
Зітос

Виклик функцій LAPACK є дуже стомлюючим та схильним до помилок. Існує кілька зручних для C ++ обгортки для LAPACK, які забезпечують набагато простіше використання, наприклад, Armadillo . Про конкретний випадок використання складного розладу власних властивостей див. Зручну для користувача функцію eig_gen () , яка внизу загортає цю чудовисько LAPACK, джеев (JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INFO), і переформатує отримані власні значення та власні вектори в стандартні подання.
hbrerkere

Відповіді:


18

Я збираюся не погодитися з деякими іншими відповідями і скажу, що вважаю, що з'ясування способу використання LAPACK є важливим у галузі наукових обчислень.

Однак існує велика крива навчання використання LAPACK. Це тому, що написано на дуже низькому рівні. Недоліком цього є те, що воно здається дуже вибагливим і не приємним для почуттів. Перевага його в тому, що інтерфейс однозначний і в основному ніколи не змінюється. Крім того, реалізація LAPACK, наприклад, бібліотека ядер Intel Math , дійсно швидка.

Для своїх цілей я маю власні класи C ++ вищого рівня, які обертаються навколо підпрограм LAPACK. Багато наукових бібліотек також використовують LAPACK під ним. Іноді їх просто використовувати просто, але, на мою думку, в розумінні цього інструменту є велика цінність. З цією метою я запропонував невеликий робочий приклад, написаний на C ++, використовуючи LAPACK, щоб розпочати роботу. Це працює в Ubuntu, із 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, все це доведеться змінити.
  • Деякі люди можуть запропонувати використовувати C-інтерфейс для LAPACK . Вони можуть мати рацію, але я завжди робив це так.

3
Багато того, що ви шукаєте, можна знайти за допомогою швидкого Googlage. Можливо, ви просто не впевнені, що шукати. Netlib є хранителем LAPACK. Документацію можна знайти тут . На цій сторінці є зручна таблиця основної функціональності LAPACK. Деякі з найважливіших з них - (1) розв’язування систем рівнянь, (2) власні значення значення, (3) сингулярні декомпозиції значення та (4) QR-факторизації. Ви розуміли посібник для DGEEV?
LedHead

1
Всі вони просто різні інтерфейси до одного і того ж. LAPACK - оригінал. Це написано у Fortran, тому для його використання вам доведеться грати в деякі ігри, щоб зробити перехресне компілювання з C / C ++ роботи, як я показав. Я ніколи не використовував LAPACKE, але схоже, що це досить тонка обгортка C над LAPACK, що дозволяє уникнути цього крос-компіляційного бізнесу, але це все ще досить низький рівень. LAPACK ++, схоже, обгортка C ++ ще більш високого рівня, але я не думаю, що він навіть більше не підтримується (хтось виправить мене, якщо я помиляюся).
LedHead

1
Я не знаю про якийсь конкретний збір коду. Але якщо ви Google будь-яке з імен підпрограми LAPACK, ви завжди знайдете старе питання на одному з сайтів StackExchange.
LedHead

1
@AlirezaHashemi До речі, причина, яку ви повинні надати WORK-масиву, полягає в тому, що LAPACK, як правило, не виділяє жодної пам'яті у своїх підпрограмах. Якщо ми використовуємо LAPACK, ми, швидше за все, використовуємо генетичну пам’ять, а розподіл пам’яті коштує дорого, тому є сенс дозволити процедурам виклику відповідати за розподіл пам'яті. Оскільки DGEEV потребує пам'яті для зберігання проміжних кількостей, ми повинні забезпечити їй робочий простір.
LedHead

1
Зрозумів. І я успішно написав свій перший код для обчислення власних значень складної матриці за допомогою zgeev. І вже роблю більше! Дякую!
Аліреза

7

Зазвичай я протистояти розповідати людям, що я думаю, що вони повинні робити, а не відповідати на їх запитання, але в цьому випадку я зроблю виняток.

Lapack написаний на FORTRAN, а API дуже подібний до FORTRAN. Існує C API для Lapack, який робить інтерфейс трохи менш болючим, але використовувати Lapack від C ++ ніколи не буде приємно.

Крім того, існує бібліотека матричних класів C ++ під назвою Eigen, яка має багато можливостей Lapack, забезпечує обчислювальну ефективність, порівнянну з кращими реалізаціями Lapack, і дуже зручна у використанні з 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. Ви можете переглянути приклад FORTRAN та результати цієї проблеми на прикладі dgeev та зробити власні порівняння.


Дякую за вашу відповідь та пояснення! Я спробую цю бібліотеку і виберу ту, яка найбільше відповідає моїм потребам.
Аліреза

О, вони перевантажують operator,! Ніколи не бачив, що робиться на практиці :-)
Вольфганг Бангерт

1
Насправді, це operator,перевантаження цікавіше / краще, ніж може здатися спочатку. Він використовується для ініціалізації матриць. Записи, які ініціалізують матрицю, можуть бути скалярними константами, але також можуть бути раніше визначеними матрицями або субматрицями. Дуже схожий на MATLAB. Побажаю, щоб моя здатність до програмування на C ++ була достатньою для того, щоб реалізувати щось, що самому витончено ;-)
Білл Грін

7

Ось ще одна відповідь у тому ж дусі, що і вище.

Ви повинні заглянути в бібліотеку лінійної алгебри Armadillo C ++ .

Плюси:

  1. Синтаксис функції на високому рівні (подібний до MATLAB). Так що ніяких DGESVмамбо-джамбо, просто X = solve( A, B )(хоча для цих дивовижних назв функцій LAPACK є причина ).
  2. Реалізує різні матричні декомпозиції (LU, QR, власні значення, SVD, Чолеський тощо)
  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;
}

Дякую за вашу відповідь та пояснення! Я спробую цю бібліотеку і виберу ту, яка найбільше відповідає моїм потребам.
Аліреза
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.