Понимание вызовов 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%, и каждая строка кода стоила того.