Эффективное решение памяти C++ для системы линейной алгебры Ax=b

Я использую привязки числовой библиотеки для Boost UBlas для решения простой линейной системы. Следующее отлично работает, за исключением того, что оно ограничено обработкой матриц A (m x m) для относительно маленькая "м".

на практике у меня есть гораздо большая матрица с размерностью m= 10^6 (до 10^7).
Существует ли подход C++ для решения Ax=b, который эффективно использует память.

#include<boost/numeric/ublas/matrix.hpp>
#include<boost/numeric/ublas/io.hpp>
#include<boost/numeric/bindings/traits/ublas_matrix.hpp>
#include<boost/numeric/bindings/lapack/gesv.hpp>
#include <boost/numeric/bindings/traits/ublas_vector2.hpp>

// compileable with this command


//g++ -I/home/foolb/.boost/include/boost-1_38 -I/home/foolb/.boostnumbind/include/boost-numeric-bindings solve_Axb_byhand.cc -o solve_Axb_byhand -llapack


namespace ublas = boost::numeric::ublas;
namespace lapack= boost::numeric::bindings::lapack;


int main()
{
    ublas::matrix<float,ublas::column_major> A(3,3);
    ublas::vector<float> b(3);


    for(unsigned i=0;i < A.size1();i++)
        for(unsigned j =0;j < A.size2();j++)
        {
            std::cout << "enter element "<<i << j << std::endl;
            std::cin >> A(i,j);
        }

    std::cout << A << std::endl;

    b(0) = 21; b(1) = 1; b(2) = 17;

    lapack::gesv(A,b);

    std::cout << b << std::endl;


    return 0;
}

6 ответов


короткий ответ: не использовать boost в LAPACK привязки, они были разработаны для плотных матриц, не разреженные матрицы, используйте UMFPACK вместо.

ответ: UMFPACK является одной из лучших библиотек для решения Ax=b, Когда A большой и разреженный.

Ниже приведен пример кода (на основе umfpack_simple.c), который генерирует простой A и b и решает Ax = b.

#include <stdlib.h>
#include <stdio.h>
#include "umfpack.h"

int    *Ap; 
int    *Ai;
double *Ax; 
double *b; 
double *x; 

/* Generates a sparse matrix problem: 
   A is n x n tridiagonal matrix
   A(i,i-1) = -1;
   A(i,i) = 3; 
   A(i,i+1) = -1; 
*/
void generate_sparse_matrix_problem(int n){
  int i;  /* row index */ 
  int nz; /* nonzero index */
  int nnz = 2 + 3*(n-2) + 2; /* number of nonzeros*/
  int *Ti; /* row indices */ 
  int *Tj; /* col indices */ 
  double *Tx; /* values */ 

  /* Allocate memory for triplet form */
  Ti = malloc(sizeof(int)*nnz);
  Tj = malloc(sizeof(int)*nnz);
  Tx = malloc(sizeof(double)*nnz);

  /* Allocate memory for compressed sparse column form */
  Ap = malloc(sizeof(int)*(n+1));
  Ai = malloc(sizeof(int)*nnz);
  Ax = malloc(sizeof(double)*nnz);

  /* Allocate memory for rhs and solution vector */
  x = malloc(sizeof(double)*n);
  b = malloc(sizeof(double)*n);

  /* Construct the matrix A*/
  nz = 0;
  for (i = 0; i < n; i++){
    if (i > 0){
      Ti[nz] = i;
      Tj[nz] = i-1;
      Tx[nz] = -1;
      nz++;
    }

    Ti[nz] = i;
    Tj[nz] = i;
    Tx[nz] = 3;
    nz++;

    if (i < n-1){
      Ti[nz] = i;
      Tj[nz] = i+1;
      Tx[nz] = -1;
      nz++;
    }
    b[i] = 0;
  }
  b[0] = 21; b[1] = 1; b[2] = 17;
  /* Convert Triplet to Compressed Sparse Column format */
  (void) umfpack_di_triplet_to_col(n,n,nnz,Ti,Tj,Tx,Ap,Ai,Ax,NULL);

  /* free triplet format */ 
  free(Ti); free(Tj); free(Tx);
}


int main (void)
{
    double *null = (double *) NULL ;
    int i, n;
    void *Symbolic, *Numeric ;
    n = 500000;
    generate_sparse_matrix_problem(n);
    (void) umfpack_di_symbolic (n, n, Ap, Ai, Ax, &Symbolic, null, null);
    (void) umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, null, null);
    umfpack_di_free_symbolic (&Symbolic);
    (void) umfpack_di_solve (UMFPACK_A, Ap, Ai, Ax, x, b, Numeric, null, null);
    umfpack_di_free_numeric (&Numeric);
    for (i = 0 ; i < 10 ; i++) printf ("x [%d] = %g\n", i, x [i]);
    free(b); free(x); free(Ax); free(Ai); free(Ap);
    return (0);
}

функции generate_sparse_matrix_problem создает матрицу A и правая сторона b. Матрица сначала строится в триплетной форме. Этот векторы Ti, Tj и Tx полностью описывают A. Триплетная форма проста в создании, но эффективные методы разреженной матрицы требуют сжатого формата разреженных столбцов. Преобразование выполняется с umfpack_di_triplet_to_col.

символическая факторизация выполняется с помощью umfpack_di_symbolic. Разреженный LU разложение A осуществляется с umfpack_di_numeric. Нижние и верхние треугольные решения выполняются с помощью umfpack_di_solve.

С n как 500,000, на моей машине, вся программа занимает около секунды, чтобы запустить. Valgrind сообщает, что 369,239,649 байт (чуть более 352 MB) были выделены.

Примечание этот страница обсуждает поддержку Boost для разреженных матриц в триплете (координата) и сжатый формат. Если хотите, вы можете написать процедуры для преобразования этих объектов boost к простым массивам UMFPACK требует в качестве входных данных.


предполагая, что ваши огромные матрицы разрежены, которые, я надеюсь, они такого размера, взгляните на PARDISO проект, который является разреженным линейным решателем, это то, что вам понадобится, если вы хотите обрабатывать матрицы такого размера, как вы сказали. Позволяет эффективно хранить только ненулевые значения и намного быстрее, чем решать ту же систему плотных матриц.


Я предполагаю, что ваша матрица плотная. Если он разрежен, вы можете найти множество специализированных алгоритмов, как уже упоминалось DeusAduro и duffymo.

Если у вас нет (достаточно большого) кластера в вашем распоряжении, вы хотите посмотреть на неосновные алгоритмы. ScaLAPACK имеет несколько неосновных решателей в составе its пакета прототип см. документацию здесь и Google дополнительные подробности. Поиск в интернете "out-of-core LU / (matrix) solvers / packages" даст вам ссылки на множество других алгоритмов и инструментов. Я в этом не специалист.

для этой проблемы, большинство людей, однако, использовать кластера. Пакет, который вы найдете почти в любом кластере, - это ScaLAPACK. Кроме того, в типичном кластере обычно есть множество других пакетов, поэтому вы можете выбрать то, что подходит вашей проблеме (примеры здесь и здесь).

прежде чем начать кодирование, вы, вероятно, хотите быстро проверить, сколько времени потребуется для решения вашей проблемы. Типичный решатель принимает около O (3*N^3) провалов (N-размерность матрицы). Если N = 100000, вы, следовательно, смотрите на 3000000 Gflops. Предполагая, что ваш решатель в памяти делает 10 Gflops/s на ядро, вы смотрите на 3 1/2 дня на одном ядре. Поскольку алгоритмы хорошо масштабируются, увеличение количества ядер должно сократить время, близкое к линейному. На вершине это идет I / O.


Не уверен в реализациях C++, но есть несколько вещей, которые вы можете сделать, если проблема с памятью зависит от типа матрицы, с которой Вы имеете дело:

  1. Если ваша матрица разрежена или полосатая, вы можете использовать разреженный или полосовой решатель. Они не хранят нулевые элементы вне диапазона.
  2. вы можете использовать решатель волнового фронта, который хранит матрицу на диске и только вводит волновой фронт матрицы для декомпозиции.
  3. вы можете избежать решения матрица в целом и использовать итерационные методы.
  4. вы можете попробовать методы Монте-Карло решения.

посмотреть список свободно доступного программного обеспечения для решения задач линейной алгебры, составленном Джек Донгарра и Хатем Абделли.

Я думаю, что для размера проблемы, на которую вы смотрите, вам, вероятно, нужен итерационный алгоритм. Если вы не хотите хранить матрицу A В разреженном формате, вы можете использовать реализацию без матрицы. Итеративные алгоритмы обычно не нуждаются в доступе к отдельным записям матрицы A, им нужно только вычислить матрично-векторные произведения Av (а иногда A^T v, произведение транспонированной матрицы с вектором). Поэтому, если библиотека хорошо спроектирована, ее должно быть достаточно, если вы передадите ей класс, который знает, как делать матрично-векторные продукты.


как следует из принятого ответа, есть UMFPACK. Но если вы используете BOOST, вы все равно можете использовать компактные матрицы в BOOST и использовать UMFPACK для решения системы. Существует привязка, которая делает его легким:

http://mathema.tician.de/software/boost-numeric-bindings

его около двух лет, но его просто привязка (наряду с несколькими другими).

см. вопрос: UMFPACK и BOOST's uBLAS разреженные Матрица