C ++ Эффективное для памяти решение для системы линейных алгебр Ax = b - PullRequest
8 голосов
/ 07 августа 2009

Я использую привязки числовых библиотек для 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 ]

14 голосов
/ 14 августа 2009

Краткий ответ: не используйте привязки 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. Триплетную форму легко создать, но эффективные методы разреженной матрицы требуют формата Compressed Sparse Column. преобразование выполняется с umfpack_di_triplet_to_col.

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

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

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

6 голосов
/ 11 августа 2009

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

Если у вас нет (достаточно большого) кластера в вашем распоряжении, вы хотите взглянуть на алгоритмы вне ядра. ScaLAPACK имеет несколько решателей вне ядра в составе прототипа пакета , подробности смотрите в документации здесь и Google, Поиск в Интернете «не связанных с ядром LU / (матричных) решателей / пакетов» даст вам ссылки на множество других алгоритмов и инструментов. Я не специалист по этим вопросам.

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

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

6 голосов
/ 07 августа 2009

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

3 голосов
/ 12 августа 2009

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

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

3 голосов
/ 07 августа 2009

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

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

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

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

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

см. Связанный вопрос: UMFPACK и UBLAS разреженная матрица BOOST

...