【问题标题】:C++ Memory Efficient Solution for Ax=b Linear Algebra SystemAx=b 线性代数系统的 C++ 内存高效解决方案
【发布时间】:2013-10-12 18:28:07
【问题描述】:

我正在使用 Boost UBlas 的数值库绑定来解决一个简单的线性系统。 以下工作正常,除了它仅限于处理矩阵 A(m x m) 相对 小“m”。

实际上,我有一个更大的矩阵,尺寸 m= 10^6(最多 10^7)。
是否存在有效使用内存的解决 Ax=b 的现有 C++ 方法。

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

【问题讨论】:

  • 指出显而易见的,一个矩阵大小为 4x10^12 到 4x10^14 字节,或者单独一个矩阵为 4 到 400 TB。 (除非,如下所述,它的稀疏)

标签: c++ boost linear-algebra lapack umfpack


【解决方案1】:

简短回答:不要使用 Boost 的 LAPACK 绑定,它们是为密集矩阵设计的, 不是稀疏矩阵,请改用UMFPACK

长答案:UMFPACK 是在 A 大且稀疏时解决 Ax=b 的最佳库之一。

以下是生成简单的Ab 的示例代码(基于umfpack_simple.c) 并解决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 执行符号分解。稀疏的 A 的 LU 分解是用 umfpack_di_numeric 执行的。 使用umfpack_di_solve 执行下三角和上三角求解。

n 为 500,000,在我的机器上,整个程序运行大约需要一秒钟。 Valgrind 报告说分配了 369,239,649 字节(略多于 352 MB)。

注意page 讨论了 Boost 在 Triplet(坐标)中对稀疏矩阵的支持 和压缩格式。如果你喜欢,你可以编写例程来转换这些 boost 对象 到简单数组UMFPACK 需要作为输入。

【讨论】:

    【解决方案2】:

    假设你的大矩阵是稀疏的,我希望它们是这样的大小,看看PARDISO 项目,它是一个稀疏线性求解器,如果你想处理这么大的矩阵,这就是你所需要的如你所说。只允许有效存储非零值,并且比求解相同的密集矩阵系统要快得多。

    【讨论】:

    • 更不用说天真的解决方案的 O(m^3) 时间复杂度了!即使是 Knuth 所说的聪明人也是 O(m^2.7ish)...如果这些矩阵不是稀疏的,您需要一个集群和一个一流的数值分析员...
    • +1 用于稀疏矩阵的想法。我在PARDISO论文中找到了numerus库和其他比较关于比较各种稀疏矩阵库ftp.numerical.rl.ac.uk/pub/reports/ghsRAL200505.pdf这可用于查找其他公认的稀疏矩阵库。
    【解决方案3】:

    我假设您的矩阵是密集的。如果它是稀疏的,您可以找到DeusAduroduffymo 已经提到的许多专用算法。

    如果您没有(足够大的)集群可供您使用,则需要查看核外算法。 ScaLAPACK 有一些核外求解器作为其 prototype package 的一部分,有关更多详细信息,请参阅文档 hereGoogle。在网络上搜索“核外 LU /(矩阵)求解器 / 包”将为您提供更多算法和工具的链接。我不是这方面的专家。

    但是,对于这个问题,大多数人会使用集群。您在几乎任何集群上都可以找到的软件包是 ScaLAPACK。此外,典型集群上通常还有许多其他包,因此您可以选择适合您问题的包(例如herehere)。

    在开始编码之前,您可能想快速检查解决问题需要多长时间。一个典型的求解器大约需要 O(3*N^3) 次翻转(N 是矩阵的维数)。如果 N = 100000,那么您将看到 3000000 Gflops。假设您的内存求解器每个内核执行 10 Gflops/s,那么您在单个内核上查看 3 1/2 天。随着算法的良好扩展,增加内核数量应该会减少接近线性的时间。最重要的是 I/O。

    【讨论】:

    • 警告:上述 O(3*N^3) 假设您使用复数。对于实数,将所有内容除以 6,即 O(0.5 * N^3) 左右。
    【解决方案4】:

    不确定 C++ 实现,但如果内存是一个问题,取决于您处理的矩阵类型,您可以做几件事:

    1. 如果您的矩阵是稀疏或带状的,您可以使用稀疏或带宽求解器。它们不会在带外存储零元素。
    2. 您可以使用波前求解器,它将矩阵存储在磁盘上,并且只引入矩阵波前进行分解。
    3. 您可以完全避免求解矩阵并使用迭代方法。
    4. 您可以尝试蒙特卡罗解决方案。

    【讨论】:

    • @duffymo:谢谢。我已经查看了 C++ 中的迭代方法实现,但它们仍然需要将其存储在矩阵中。 freenet-homepage.de/guwi17/ublas/examples 如果我错了,你知道 C++ 中任何 mem 高效的迭代实现吗?
    • 正确,愚蠢的小子。我应该记得的。我会研究并行算法,因为将工作分配给 N 个处理器并将其重新组合在一起以获得结果的问题与将其临时移出磁盘的问题密切相关。
    【解决方案5】:

    查看由 Jack Dongarra 和 Hatem Ltaief 编写的 list of freely available software for the solution of linear algebra problems

    我认为对于您正在查看的问题规模,您可能需要一个迭代算法。如果您不想以稀疏格式存储矩阵 A,则可以使用无矩阵实现。迭代算法通常不需要访问矩阵 A 的各个条目,它们只需要计算矩阵向量乘积 Av(有时是 A^T v,转置矩阵与向量的乘积)。因此,如果该库设计得很好,那么如果您向它传递一个知道如何进行矩阵向量乘积的类就足够了。

    【讨论】:

      【解决方案6】:

      正如公认的答案所暗示的那样,有 UMFPACK。但是如果您使用 BOOST,您仍然可以使用 BOOST 中的紧凑矩阵并使用 UMFPACK 来求解系统。有一个绑定使它变得容易:

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

      它大约有两年的历史,但它只是一个绑定(连同其他一些)。

      查看相关问题: UMFPACK and BOOST's uBLAS Sparse Matrix

      【讨论】:

        猜你喜欢
        • 2016-06-15
        • 1970-01-01
        • 1970-01-01
        • 2016-01-30
        • 2013-04-18
        • 1970-01-01
        • 2019-08-14
        • 2015-07-18
        • 1970-01-01
        相关资源
        最近更新 更多