วิธีเริ่มใช้ LAPACK ใน c ++


10

ฉันยังใหม่กับวิทยาศาสตร์การคำนวณและฉันได้เรียนรู้วิธีการพื้นฐานสำหรับการรวมการแก้ไขวิธีการเช่น RK4, Numerov และอื่น ๆ ใน c ++ แต่เมื่อเร็ว ๆ นี้อาจารย์ของฉันขอให้ฉันเรียนรู้วิธีการใช้ 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 โดยตรงน่าเบื่อมากและเกิดข้อผิดพลาดได้ง่าย มีหลายที่ใช้งานง่าย c ++ สำหรับห่อ LAPACK ที่ให้การใช้งานมากขึ้นเช่นตัวนิ่ม สำหรับกรณีการใช้งานที่เฉพาะเจาะจงของการสลายตัวของไอเก็นที่ซับซ้อนดูฟังก์ชั่นที่ใช้งานง่ายeig_gen ()ซึ่งอยู่ภายใต้ wraps monstrosity LAPACK นี้ zheev (JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RORK, INFO) และปรับรูปแบบค่าลักษณะเฉพาะและ eigenvector ที่เป็นตัวแทนมาตรฐาน
hbrerkere

คำตอบ:


18

ฉันจะไม่เห็นด้วยกับคำตอบอื่น ๆ และบอกว่าฉันเชื่อว่าการหาวิธีใช้ LAPACK นั้นมีความสำคัญในด้านการคำนวณทางวิทยาศาสตร์

อย่างไรก็ตามมีช่วงการเรียนรู้ขนาดใหญ่สำหรับการใช้ LAPACK นี่เป็นเพราะมันถูกเขียนในระดับต่ำมาก ข้อเสียของมันคือมันดูลึกลับมากและไม่พอใจกับความรู้สึก ข้อดีของมันคืออินเทอร์เฟซไม่ชัดเจนและโดยทั่วไปจะไม่เปลี่ยนแปลง นอกจากนี้การใช้งาน LAPACK เช่นไลบรารี Intel Math Kernelนั้นรวดเร็วมาก

สำหรับวัตถุประสงค์ของฉันฉันมีคลาส C ++ ระดับสูงกว่าของฉันซึ่งล้อมรอบรูทีนย่อย LAPACK ห้องสมุดวิทยาศาสตร์หลายแห่งใช้ LAPACK ภายใต้ บางครั้งมันง่ายกว่าที่จะใช้มัน แต่ในความคิดของฉันมีค่ามากในการทำความเข้าใจเครื่องมือที่อยู่ด้านล่าง ด้วยเหตุนี้ฉันจึงได้ยกตัวอย่างการทำงานเล็ก ๆ ที่เขียนใน C ++ โดยใช้ LAPACK เพื่อให้คุณเริ่มต้นได้ สิ่งนี้ใช้ได้ใน Ubuntu พร้อมกับliblapack3แพ็คเกจที่ติดตั้งและแพ็คเกจอื่น ๆ ที่จำเป็นสำหรับการสร้าง มันอาจถูกใช้ในลีนุกซ์ส่วนใหญ่, แต่การติดตั้ง 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 factorizations คุณเข้าใจคู่มือสำหรับ 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 แล้วทำอะไรได้มากกว่านี้! ขอบคุณ!
Alireza

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

ตัวอย่าง eigenvalue dgeevปัญหานี้เป็นกรณีทดสอบสำหรับฟังก์ชัน คุณสามารถดูรหัส FORTRAN และผลลัพธ์สำหรับตัวอย่างปัญหานี้ และทำการเปรียบเทียบของคุณเอง


ขอบคุณสำหรับคำตอบและคำอธิบาย! ฉันจะลองห้องสมุดนี้และเลือกห้องสมุดที่เหมาะสมกับความต้องการของฉันมากที่สุด
Alireza

โอ้พวกเขามากเกินไปoperator,! ไม่เคยเห็นที่ทำในทางปฏิบัติจริง :-)
Wolfgang Bangerth

1
ที่จริงแล้วการoperator,โอเวอร์โหลดนั้นน่าสนใจกว่า / ดีกว่าที่อาจปรากฏขึ้นเป็นครั้งแรก มันถูกใช้เพื่อเริ่มต้นการฝึกอบรม รายการที่เริ่มต้นเมทริกซ์สามารถเป็นค่าคงที่สเกลาร์ แต่ยังสามารถเป็นเมทริกซ์ที่กำหนดไว้ก่อนหน้านี้หรือเมทริกซ์ย่อย เหมือน MATLAB มาก ต้องการความสามารถในการเขียนโปรแกรม C ++ ของฉันนั้นดีพอที่จะใช้สิ่งที่ซับซ้อนตัวเอง ;-)
Bill Greene

7

นี่คือคำตอบอื่นในหลอดเลือดดำเช่นเดียวกับข้างต้น

คุณควรจะมองเข้าไปในตัวนิ่ม c ++ เชิงเส้นห้องสมุดพีชคณิต

ข้อดี:

  1. ไวยากรณ์ของฟังก์ชั่นเป็นระดับสูง (คล้ายกับ MATLAB) ดังนั้นจึงไม่มีDGESVmumbo-jumbo เพียง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.