在 R 中创建 Block Toeplitz 矩阵的更有效方法?

机器算法验证 r 矩阵 托普利茨
2022-03-20 03:30:31

我正在尝试创建一个 Block Toeplitz 矩阵,如下所示: 其中 ,是一个矩阵。U=(U(0)U(1)U(n)U(1)TU(0)U(n1)U(n)TU(n1)TU(0))U(i)i=1,,nk×k

我发现 R 中的 toeplitz() 函数只处理是标量的情况。目前我只知道使用循环来创建矩阵,这在 R 中效率不高。如果有人对矢量化代码有任何建议,请您帮帮我。U(i)U

例如,我有兴趣创建这样的矩阵:

, ,让对于U(0)=(0.8100.8)Φ=(0.9100.8)U(i)=ΦiU(0)i=1,,n

我写的R代码是,

U0 <- matrix(c(0.8, 0, 1, 0.8), 2, 2) # create U0
phi <- matrix(c(0.9, 0, 1, 0.8), 2, 2) # create phi
n <- 100 # n is the number of U(i)
k <- dim(U0)[1]
U <- matrix(NA, k*n, k*n) # create an empty matrix to receive value
for (i in 1:n) {
   for (j in 1:n) {
     if (i >= j) {
       U[((i-1)*k+1):(i*k), ((j-1)*k+1):(j*k)] <- phi^(i-j) %*% U0
     } else { 
       U[((i-1)*k+1):(i*k), ((j-1)*k+1):(j*k)] <- t(phi)^(j-i) %*% U0
     }
   }
 }

有没有办法避免循环?

谢谢。

2个回答

这个问题可能会引起统计学家和数据科学家的兴趣,因为它提出了多维数组处理问题。

循环不是问题。 您的目标可能是高效地执行操作,不管它是如何完成的,并且可能以一种明显可并行的方式来完成。

利用 R 管理多维数组的能力。 (它从 FORTRAN 继承了这一点,即使在其原始设计中也可以处理多达七个维度。)将数据组整体移动到一个数组中,然后根据需要对其进行整形。即使需要进行一些循环,这些本机操作也会很快。

尽管它使用短的高级循环(每个循环执行次),但以下解决方案将创建一个个块的“block Toeplitz”数组——一个包含四百万个元素的数组——在一个半秒和少量使用开销存储。它基于观察到可以从字符串构造 Toeplitz 模式n+1n=9992×2

Un,Un1,,U1,U0,U1,U2,,Un

通过取最后元素形成输出的第一列,将这些元素向左移动形成第二列输出,依此类推,直到输出的最后一列由前元素形成这个字符串。n+1n+1

该代码用于n表示(因为索引从 1 而不是 0 开始)。矩阵串本身,作为一个数组,存储在一个临时数组中。n+1Rk×(2n1)strip

最后,通过“爆破”输出列的条带部分创建数组被置换以使其值以正确的顺序存储(我懒得一开始就制定出最佳存储配置),然后重铸为数组(不涉及数据移动)。这些按列操作的每个操作都是高度矢量化的,并且能够进行大规模并行。k×k×n×nXkn×kn

的初始值U包含用于检查输出是否正确的明显模式。

#
# Create n square matrices of dimension k by k.
#
U <- list(matrix(1:4, 2), matrix(5:8, 2), matrix(9:12, 2))
#U <- lapply(1:1000, function(i) matrix(-3:0 + 4*i, 2))

system.time({
  k <- min(unlist(lapply(U, dim)))
  n <- length(U)
  #
  # Create the "strip".
  #
  strip <- array(NA, dim=c(k,k,2*n-1))
  for (i in 1:n) strip[,,i] <- U[[n+1-i]]
  if (n > 1) for (i in 2:n) strip[,,n+i-1] <- t(U[[i]])
  #
  # Assemble into "block-Toeplitz" form.
  #
  X <- array(NA, dim=c(k,k,n,n))
  #
  # Blast the strip across X.
  #
  for (i in 1:n) X[,,,i] <- strip[,,(n+1-i):(2*n-i)]
  X <- matrix(aperm(X, c(1,3,2,4)), n*k)
})

以下函数将块列表作为参数。但是,这可能不是最好的解决方案:仍然存在一个循环。而且它需要更多的工作,因为它不做块下对角线的转置(在我的例子中,我有对称矩阵)。

toeplitz.block <- function(blocks) {
    l <- length(blocks)
    m.str <- toeplitz(1:l)

    res <- lapply(1:l,function(k) {
        res <- matrix(0,ncol=ncol(m.str),nrow=nrow(m.str))
        res[m.str == k] <- 1
        res %x% blocks[[k]]
    })

    Reduce("+",res)
}