Понимание вызовов LAPACK на С++ с помощью простого примера
Я начинаю с интерфейсом LAPACK и С++/Fortran. Мне нужно решить линейные уравнения и проблемы с собственными значениями, используя LAPACK/BLAS на Mac OS-X Lion. OS-X Lion предоставляет оптимизированные библиотеки BLAS и LAPACK (в/usr/lib), и я связываю эти библиотеки, а не загружаю их из netlib.
Моя программа (воспроизведена ниже) компилируется и работает нормально, но это дает мне неправильные ответы. Я исследовал в Интернете и Stackoverflow, и проблема, возможно, связана с тем, как массивы хранилищ С++ и Fortran хранятся в разных форматах (ряд основных и крупных столбцов). Однако, как вы увидите в моем примере, простой массив для моего примера должен выглядеть одинаково в С++ и fortran. В любом случае здесь идет.
Предположим, что мы хотим решить следующую линейную систему:
x + y = 2
x - y = 0
Решение (x, y) = (1,1). Теперь я попытался решить эту проблему с помощью Lapack следующим образом
// LAPACK test code
#include<iostream>
#include<vector>
using namespace std;
extern "C" void dgetrs(char *TRANS, int *N, int *NRHS, double *A,
int *LDA, int *IPIV, double *B, int *LDB, int *INFO );
int main()
{
char trans = 'N';
int dim = 2;
int nrhs = 1;
int LDA = dim;
int LDB = dim;
int info;
vector<double> a, b;
a.push_back(1);
a.push_back(1);
a.push_back(1);
a.push_back(-1);
b.push_back(2);
b.push_back(0);
int ipiv[3];
dgetrs(&trans, &dim, &nrhs, & *a.begin(), &LDA, ipiv, & *b.begin(), &LDB, &info);
std::cout << "solution is:";
std::cout << "[" << b[0] << ", " << b[1] << ", " << "]" << std::endl;
std::cout << "Info = " << info << std::endl;
return(0);
}
Этот код был скомпилирован следующим образом:
g++ -Wall -llapack -lblas lapacktest.cpp
Тем не менее, при выполнении этого решения я получаю решение как (-2,2), что, очевидно, неверно. Я пробовал всю комбинацию перегруппировки строк/столбцов моей матрицы
a
. Также обратите внимание, что представление матрицы
a
должно быть идентичным в форматах строк и столбцов. Я думаю, что получение этого простого примера для работы позволит мне начать с LAPACK, и любая помощь будет оценена по достоинству.
Ответы
Ответ 1
Вам нужно определить матрицу (вызывая dgetrf
), прежде чем вы сможете решить систему с помощью dgetrs
. Кроме того, вы можете использовать процедуру dgesv
, которая выполняет оба действия для вас.
Кстати, вам не нужно декларировать интерфейсы самостоятельно, они находятся в заголовках ускорения:
// LAPACK test code
#include <iostream>
#include <vector>
#include <Accelerate/Accelerate.h>
using namespace std;
int main()
{
char trans = 'N';
int dim = 2;
int nrhs = 1;
int LDA = dim;
int LDB = dim;
int info;
vector<double> a, b;
a.push_back(1);
a.push_back(1);
a.push_back(1);
a.push_back(-1);
b.push_back(2);
b.push_back(0);
int ipiv[3];
dgetrf_(&dim, &dim, &*a.begin(), &LDA, ipiv, &info);
dgetrs_(&trans, &dim, &nrhs, & *a.begin(), &LDA, ipiv, & *b.begin(), &LDB, &info);
std::cout << "solution is:";
std::cout << "[" << b[0] << ", " << b[1] << ", " << "]" << std::endl;
std::cout << "Info = " << info << std::endl;
return(0);
}
Ответ 2
Для тех, кто не хочет беспокоиться об ускорительной структуре, я предоставляю код Stephen Canon (благодаря ему, конечно), ничего, кроме чистой ссылки на библиотеку
// LAPACK test code
//compile with: g++ main.cpp -llapack -lblas -o testprog
#include <iostream>
#include <vector>
using namespace std;
extern "C" void dgetrf_(int* dim1, int* dim2, double* a, int* lda, int* ipiv, int* info);
extern "C" void dgetrs_(char *TRANS, int *N, int *NRHS, double *A, int *LDA, int *IPIV, double *B, int *LDB, int *INFO );
int main()
{
char trans = 'N';
int dim = 2;
int nrhs = 1;
int LDA = dim;
int LDB = dim;
int info;
vector<double> a, b;
a.push_back(1);
a.push_back(1);
a.push_back(1);
a.push_back(-1);
b.push_back(2);
b.push_back(0);
int ipiv[3];
dgetrf_(&dim, &dim, &*a.begin(), &LDA, ipiv, &info);
dgetrs_(&trans, &dim, &nrhs, & *a.begin(), &LDA, ipiv, & *b.begin(), &LDB, &info);
std::cout << "solution is:";
std::cout << "[" << b[0] << ", " << b[1] << ", " << "]" << std::endl;
std::cout << "Info = " << info << std::endl;
return(0);
}
И о руководстве, есть полная версия PDF, доступная на веб-сайте Intel. Вот пример их документации HTML.
http://software.intel.com/sites/products/documentation/hpc/mkl/mklman/GUID-A02DB70F-9704-42A4-9071-D409D783D911.htm
Ответ 3
Если вы хотите использовать LAPACK с С++, вам может понадобиться FLENS. Он определяет интерфейсы низкого и высокого уровня для LAPACK, но также повторно реализует некоторые функции LAPACK.
С низкоуровневым интерфейсом FLENS-LAPACK вы можете использовать свои собственные типы матриц/векторов (если они имеют макет памяти с поддержкой LAPACK). Ваш вызов dgetrf
будет выглядеть следующим образом:
info = lapack::getrf(NoTrans, dim, nrhs, a.begin(), LDA, ipiv);
и для dgetrs
lapack::getrs(NoTrans, dim, nrhs, a.begin(), LDA, ipiv, b.begin(), LDB);
Таким образом, низкоуровневые функции FLENS-LAPACK перегружены относительно типов элементов. Следовательно, функция LAPACK sgetrs
, dgetrs
, cgetrs
, zgetrs
находится в низкоуровневом интерфейсе FLENS-LAPACK lapack::getrs
. Вы также передаете параметры по значению/ссылке, а не как указатель (например, LDA
вместо &LDA
).
Если вы используете матричные типы FLENS, вы можете закодировать его как
info = lapack::trf(NoTrans, A, ipiv);
if (info==0) {
lapack::trs(NoTrans, A, ipiv, b);
}
Или вы просто используете функцию драйвера LAPACK dgesv
lapack::sv(NoTrans, A, ipiv, b);
Здесь список функций FLENS-LAPACK.
Отказ от ответственности: Да, FLENS - мой ребенок! Это означает, что я закодировал около 95%, и каждая строка кода стоила того.