【问题标题】:How to construct this block tridiagonal (sparse) matrix?如何构造这个块三对角(稀疏)矩阵?
【发布时间】:2019-02-26 09:13:53
【问题描述】:

在 R 或 C++ 中是否有一种快速填充(稀疏)矩阵的方法:

A, B, 0, 0, 0
C, A, B, 0, 0
0, C, A, B, 0
0, 0, C, A, B
0, 0, 0, C, A

其中ABC 是 5x5 矩阵,0 是 5x5 零矩阵。

实际上,我使用的矩阵是数百到数千行/列。在 R 中,我知道可以使用 rbindcbind,但这是一个有点乏味和昂贵的解决方案。


更新:如何使用这个矩阵

令上面的矩阵为H。给定两个向量xs,我需要计算H %*% x + s = y

【问题讨论】:

  • 取决于...您如何保存矩阵,哪个容器,稀疏与否。更多信息会很有用......(c ++)
  • 在 c++ 中,我通常使用 Eigen。大多数情况下,矩阵是稀疏的,但有时在添加到另一个密集矩阵时会变得密集。
  • 如果我们调用上面的矩阵M,有一个向量x,和一个向量s:M*x + s = y,在下一次迭代中y变成x的每个时间步计算。跨度>
  • 我通常在 R 中编码,并且我正在构建这个模型的简单小网格以在 R 中进行测试。然后我通常将其转换为 c++ 以提高速度,但我愿意跳过 R如果绝对必要的话。我对 c++ 的熟练程度不高,所以在 R 中调试和计算数学对我来说更容易。
  • 正确。该模型有一个隐式版本,需要对系统进行求逆或求解(求解 x 并知道 y),但对于此版本,只需要矩阵向量乘法和加法。

标签: c++ r matrix sparse-matrix


【解决方案1】:

你如何使用这个矩阵实际上更重要。在许多情况下,后续计算不需要显式矩阵构造。此问答可能与您无关:How to build & store this large lower triangular matrix for matrix-vector multiplication?,但非常适合说明我的观点。

令上面的矩阵为H。给定两个向量xs,我需要计算H %*% x + s = y

矩阵只用于矩阵向量乘法?我们完全可以跳过形成这个矩阵,因为乘法只是rbind(B, A, C)x 之间的滚动矩阵向量乘法。

## `nA` is the number of `A`-blocks on the main diagonal of `H`
MatVecMul <- function (A, B, C, nA, x, s) {
  ## input validation
  if (diff(dim(A))) stop("A is not a square matrix")
  if (diff(dim(B))) stop("B is not a square matrix")
  if (diff(dim(C))) stop("C is not a square matrix")
  if (dim(A)[1] != dim(B)[1]) stop("A and B does not have the same dimension")
  if (dim(A)[1] != dim(C)[1]) stop("A and C does not have the same dimension")
  if (length(x) != nA * M) stop("dimension dismatch between matrix and vector")
  if (length(x) %% length(s)) stop("length of 'x' does not divide length of 's'")
  ## initialization
  y <- numeric(length(x))
  ##########################
  # compute `y <- H %*% x` #
  ##########################
  ## first block column contains `rbind(A, C)`
  M <- dim(A)[1]
  ind_x <- 1:M
  y[1:(2 * M)] <- rbind(A, C) %*% x[ind_x]
  ind_x <- ind_x + M
  ## middle (nA - 2) block columns contain `rbind(B, A, C)`
  BAC <- rbind(B, A, C)
  ind_y <- 1:(3 * M)
  i <- 0
  while (i < (nA - 2)) {
    y[ind_y] <- y[ind_y] + BAC %*% x[ind_x]
    ind_x <- ind_x + M
    ind_y <- ind_y + M
    i <- i + 1
    }
  ## final block column contains `rbind(A, C)`
  ind_y <- ind_y[1:(2 * M)]
  y[ind_y] <- y[ind_y] + rbind(B, A) %*% x[ind_x]
  ## compute `y + s` and return
  y + s
  }

这是一个可重现的例子。

set.seed(0)
M <- 5  ## dim of basic block
A <- matrix(runif(M * M), M)
B <- matrix(runif(M * M), M)
C <- matrix(runif(M * M), M)
nA <- 5
x <- runif(25)
s <- runif(25)

y <- MatVecMul(A, B, C, nA, x, s)

为了验证上面的y是否被正确计算,我们需要显式地构造H。构造方法有很多种。

方法一:使用块对角(稀疏)矩阵

N <- nA * M  ## dimension of the final square matrix

library(Matrix)

## construct 3 block diagonal matrices
H1 <- bdiag(rep.int(list(A), nA))
H2 <- bdiag(rep.int(list(B), nA - 1))
H3 <- bdiag(rep.int(list(C), nA - 1))

## augment H2 and H3, then add them together with H1
H <- H1 +
     rbind(cbind(Matrix(0, nrow(H2), M), H2), Matrix(0, M, N)) + 
     cbind(rbind(Matrix(0, M, ncol(H3)), H3), Matrix(0, N, M))

## verification
range((H %*% x)@x + s - y)
#[1] -8.881784e-16  8.881784e-16

我们看到MatVecMul 是正确的。

方法二:直接填写

此方法基于以下观察:

B
-------------
A  B
C  A  B
   C  A  B
      C  A  B
         C  A
-------------
            C

很容易先构造矩形矩阵,然后在中间对方阵进行子集化。

BAC <- rbind(B, A, C)

nA <- 5  ## number of basic block
N <- nA * M  ## dimension of the final square matrix
NR <- N + 2 * M  ## leading dimension of the rectangular matrix

## 1D index for the leading B-A-C block
BAC_ind1D <- c(outer(1:nrow(BAC), seq(from = 0, by = NR, length = M), "+"))
## 1D index for none-zero elements in the rectangular matrix
fill_ind1D <- outer(BAC_ind1D, seq(from = 0, by = M * (NR + 1), length = nA), "+")
## 2D index for none-zero elements in the rectangular matrix
fill_ind2D <- arrayInd(fill_ind1D, c(NR, N))

## construct "dgCMatrix" sparse matrix
library(Matrix)
Hsparse <- sparseMatrix(i = fill_ind2D[, 1], j = fill_ind2D[, 2], x = BAC)
Hsparse <- Hsparse[(M+1):(N+M), ]

## construct dense matrix
Hdense <- matrix(0, NR, N)
Hdense[fill_ind2D] <- BAC
Hdense <- Hdense[(M+1):(N+M), ]

## verification
range((Hsparse %*% x)@x + s - y)
#[1] -8.881784e-16  8.881784e-16

range(base::c(Hdense %*% x) + s - y)
#[1] -8.881784e-16  8.881784e-16

我们再次看到MatVecMul 是正确的。


使用 Rcpp 实现 MatVecMul

将 R 函数 MatVecMul 转换为 Rcpp 函数非常容易。我会把这个任务留给你,因为你已经使用了

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    • 2013-11-29
    • 2012-09-04
    • 2015-07-22
    • 2012-01-10
    • 1970-01-01
    相关资源
    最近更新 更多