Block-Diagonal Binding of Matrices

Block-diagonal binding of matrices

adiag from a package magic does what you want:

library(magic)
adiag(a,b)
[,1] [,2] [,3] [,4] [,5]
[1,] 1 3 5 0 0
[2,] 2 4 6 0 0
[3,] 0 0 0 7 9
[4,] 0 0 0 8 10

Alternatively, you could use a package Matrix and function bdiag

library(Matrix)
bdiag(a,b)
4 x 5 sparse Matrix of class "dgCMatrix"

[1,] 1 3 5 . .
[2,] 2 4 6 . .
[3,] . . . 7 9
[4,] . . . 8 10

that returns a sparse matrix and which might be more efficient. Use as.matrix(bdiag(a,b)) to get a regular one.

Build a block diagonal matrix from a known matrix

We can use bdiag

library(Matrix)
bdiag(replicate(3, B, simplify = FALSE))
#6 x 6 sparse Matrix of class "dgCMatrix"

#[1,] 1 4 . . . .
#[2,] 3 5 . . . .
#[3,] . . 1 4 . .
#[4,] . . 3 5 . .
#[5,] . . . . 1 4
#[6,] . . . . 3 5

Can we wrapped in a function

fdiag <- function(mat, n) {
bdiag(replicate(n, mat, simplify = FALSE))
}

fdiag(B, 3)

How to create a diagonal matrix with blocks in the diagonal with each block a different matrix?

We can use bdiag from Matrix after placing the matrices in a list to get a sparseMatrix.

library(Matrix)
sM <- bdiag(mget(paste0("A", 1:5)))
sM
# 10 x 10 sparse Matrix of class "dgCMatrix"

# [1,] 1 3 . . . . . . . .
# [2,] 2 4 . . . . . . . .
# [3,] . . 5 7 . . . . . .
# [4,] . . 6 8 . . . . . .
# [5,] . . . . 9 11 . . . .
# [6,] . . . . 10 12 . . . .
# [7,] . . . . . . 13 15 . .
# [8,] . . . . . . 14 16 . .
# [9,] . . . . . . . . 17 19
#[10,] . . . . . . . . 18 20

If needed, the sparseMatrix can be converted to a matrix

as.matrix(sM)

data

A1 <- matrix(1:4, 2, 2)
A2 <- matrix(5:8, 2, 2)
A3 <- matrix(9:12, 2, 2)
A4 <- matrix(13:16, 2, 2)
A5 <- matrix(17:20, 2, 2)

How could I build a function that extracts the diagonal block matrices of a larger one in R

I think a clearer solution is to reset the dimensions and then let R do the index calculations for you:

err_cov <- function(x){                                                          
m <- nrow(x)
n <- ncol(x)
#compute the full error covariance matrix as the inner product
#of vec(x) and its transpose
vec <- as.vector(x)
omega <- tcrossprod(vec)
dim(omega) <- c(n,m,n,m)
sigmas <- list()
for (j in 1:m)
sigmas[[j]] <- omega[,j,,j]

return(sigmas)
}

Here is an example:

> x
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
> tcrossprod(vec)
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 2 3 4 5 6
[2,] 2 4 6 8 10 12
[3,] 3 6 9 12 15 18
[4,] 4 8 12 16 20 24
[5,] 5 10 15 20 25 30
[6,] 6 12 18 24 30 36
> err_cov(x)
[[1]]
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 2 4 6
[3,] 3 6 9

[[2]]
[,1] [,2] [,3]
[1,] 16 20 24
[2,] 20 25 30
[3,] 24 30 36

Repeating a block matrix many times in diagonal part of a block matrix, with the off-diagonal blocks all zero matrices?

1) kronecker If M is your matrix and k is the number of times you want it repeated then:

kronecker(diag(k), M)

For example,

M <- matrix(0.5, 5, 5) + diag(0.5, 5)
k <- 2
kronecker(diag(k), M)

giving:

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 1.0 0.5 0.5 0.5 0.5 0.0 0.0 0.0 0.0 0.0
[2,] 0.5 1.0 0.5 0.5 0.5 0.0 0.0 0.0 0.0 0.0
[3,] 0.5 0.5 1.0 0.5 0.5 0.0 0.0 0.0 0.0 0.0
[4,] 0.5 0.5 0.5 1.0 0.5 0.0 0.0 0.0 0.0 0.0
[5,] 0.5 0.5 0.5 0.5 1.0 0.0 0.0 0.0 0.0 0.0
[6,] 0.0 0.0 0.0 0.0 0.0 1.0 0.5 0.5 0.5 0.5
[7,] 0.0 0.0 0.0 0.0 0.0 0.5 1.0 0.5 0.5 0.5
[8,] 0.0 0.0 0.0 0.0 0.0 0.5 0.5 1.0 0.5 0.5
[9,] 0.0 0.0 0.0 0.0 0.0 0.5 0.5 0.5 1.0 0.5
[10,] 0.0 0.0 0.0 0.0 0.0 0.5 0.5 0.5 0.5 1.0

1a) %x% The last line of code can alternately be written as:

diag(k) %x% M

2) Matrix::bdiag Another possibility if you want to save space is to create a sparse matrix of class "dgMCatrix". It does not store the zero values. See ?bdiag :

library(Matrix)

bdiag(replicate(k, M, simplify = FALSE))

giving:

10 x 10 sparse Matrix of class "dgCMatrix"

[1,] 1.0 0.5 0.5 0.5 0.5 . . . . .
[2,] 0.5 1.0 0.5 0.5 0.5 . . . . .
[3,] 0.5 0.5 1.0 0.5 0.5 . . . . .
[4,] 0.5 0.5 0.5 1.0 0.5 . . . . .
[5,] 0.5 0.5 0.5 0.5 1.0 . . . . .
[6,] . . . . . 1.0 0.5 0.5 0.5 0.5
[7,] . . . . . 0.5 1.0 0.5 0.5 0.5
[8,] . . . . . 0.5 0.5 1.0 0.5 0.5
[9,] . . . . . 0.5 0.5 0.5 1.0 0.5
[10,] . . . . . 0.5 0.5 0.5 0.5 1.0

2b) Diagonal or to create a sparse matrix of class "dgTMatrix" :

Diagonal(k) %x% M

Combine matrices at corner in R?

We could use bdiag

library(Matrix)
as.matrix(bdiag(m1, m2))

-output

     [,1] [,2] [,3] [,4]
[1,] 1.0 0.5 0.00 0.00
[2,] 0.5 1.0 0.00 0.00
[3,] 0.0 0.0 0.50 0.25
[4,] 0.0 0.0 0.25 0.50

construct new supermatrix from block matrices

I tried writing a general purpose function. The usage is similar to matrix() but the first argument is a list of matrices (or vectors that will be recycled). It does not have all the bells and whistles (dimnames, byrow) but it is a decent start. I wouldn't be surprised to find out a better and more complete function already exists in a package but at least it was a fun exercise:

supermatrix <- function(list.of.mat, nrow = 1L, ncol = 1L) {
stopifnot(length(list.of.mat) == nrow * ncol)
is.mat <- vapply(list.of.mat, is.matrix, logical(1L))
is.vec <- vapply(list.of.mat, is.vector, logical(1L))
if (any(!is.mat & !is.vec)) stop("the list items must be matrices or vectors")
is.mat.mat <- matrix(is.mat, nrow, ncol)
if (any(rowSums(is.mat.mat) == 0L))
stop("we need at least one matrix per super row")
if (any(colSums(is.mat.mat) == 0L))
stop("we need at least one matrix per super column")
na.mat <- matrix(NA, nrow, ncol)
nrow.mat <- replace(na.mat, is.mat, vapply(list.of.mat[is.mat], nrow, integer(1L)))
ncol.mat <- replace(na.mat, is.mat, vapply(list.of.mat[is.mat], ncol, integer(1L)))
is.not.uniq <- function(x) length(table(x)) > 1L
if (any(apply(nrow.mat, 1, is.not.uniq))) stop("row dim mismatch")
if (any(apply(ncol.mat, 2, is.not.uniq))) stop("col dim mismatch")
nrow.vec <- rowMeans(nrow.mat, na.rm = TRUE)
ncol.vec <- colMeans(ncol.mat, na.rm = TRUE)
nrow.mat <- matrix(nrow.vec, nrow, ncol, byrow = FALSE)
ncol.mat <- matrix(ncol.vec, nrow, ncol, byrow = TRUE)
all.mat <- Map(matrix, list.of.mat, nrow.mat, ncol.mat)
i1.idx <- unlist(Map(rep, row(na.mat), lapply(all.mat, length)))
j1.idx <- unlist(Map(rep, col(na.mat), lapply(all.mat, length)))
i2.idx <- unlist(lapply(all.mat, row))
j2.idx <- unlist(lapply(all.mat, col))
o.idx <- order(j1.idx, j2.idx, i1.idx, i2.idx)
matrix(unlist(all.mat)[o.idx], sum(nrow.vec), sum(ncol.vec))
}

Example usage:

A <- matrix(1:9,nrow=3,ncol=3)
B <- matrix(5:10,nrow=2,ncol=3)
C <- matrix(11:20,nrow=2,ncol=5)

supermatrix(list(A, B, 0, C), 2, 2)
supermatrix(list(A, B, A, 1, 0, C, 2, C), 4, 2)

vector into block matrix

Instead of manually doing the split, we can use %/%

k <- 3
lst <- split(a, (seq_along(a)-1)%/%k + 1)
do.call(rbind, lapply(split(lst, (seq_along(lst)-1) %/% k + 1),
function(x) do.call(cbind, lapply(x, function(y) diag(y)))))
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
# [1,] 1 0 0 2 0 0 3 0 0
# [2,] 0 1 0 0 2 0 0 3 0
# [3,] 0 0 1 0 0 2 0 0 3
# [4,] 2 0 0 4 0 0 6 0 0
# [5,] 0 2 0 0 4 0 0 6 0
# [6,] 0 0 2 0 0 4 0 0 6
# [7,] 3 0 0 6 0 0 9 0 0
# [8,] 0 3 0 0 6 0 0 9 0
# [9,] 0 0 3 0 0 6 0 0 9

generating efficient sub block matrices to avoid duplicated code

Given the periodic nature of the data the submatrices and the whole block matrix can be filled via the PAD= argumant to the RESHAPE intrinsic.

program blox
implicit none
integer m
integer n
integer, allocatable :: zero(:), sp3(:,:,:)
integer, allocatable :: b1(:,:), b2(:,:), b3(:,:)
integer, allocatable :: iKE(:,:)
character(80) fmt
m = 4
n = m**2
zero = reshape([integer::],[m+1],pad=[0])
b1 = reshape([integer::],[m,m],pad=[-4,1,zero(1:m-2),1])
b2 = reshape([integer::],[m,m],pad=[1,zero(1:m)])
b3 = reshape([integer::],[m,m],pad=zero(1:m+1))
sp3 = reshape([integer::],[m,m,m-2],pad=b3)
iKE = reshape(reshape([integer::],[m,m,m,m],pad=[b1,b2,sp3,b2],order=[1,3,2,4]),[n,n])
write(fmt,'(*(g0))') '(',m,'(',m,'(',m,'i3:1x)/))'
write(*,fmt) transpose(iKE)
end program blox

Notice how strings of zeros are created by padding out an empty array with zeros (zero) and then arrays with periodic data are filled by padding out an empty array array with a single period of data (bl, b2, and b3). Then a matrix consisting of copies of a block matrix is created by padding out an empty array with the block (sp3). Finally a block-periodic matrix is created by padding out an empty array with the sequence of blocks. The resulting matrix has to be read out in cross-order and then reshaped into the right dimensions (iKE).

Output:

 -4  1  0  0   1  0  0  0   0  0  0  0   0  0  0  0
1 -4 1 0 0 1 0 0 0 0 0 0 0 0 0 0
0 1 -4 1 0 0 1 0 0 0 0 0 0 0 0 0
0 0 1 -4 0 0 0 1 0 0 0 0 0 0 0 0

1 0 0 0 -4 1 0 0 1 0 0 0 0 0 0 0
0 1 0 0 1 -4 1 0 0 1 0 0 0 0 0 0
0 0 1 0 0 1 -4 1 0 0 1 0 0 0 0 0
0 0 0 1 0 0 1 -4 0 0 0 1 0 0 0 0

0 0 0 0 1 0 0 0 -4 1 0 0 1 0 0 0
0 0 0 0 0 1 0 0 1 -4 1 0 0 1 0 0
0 0 0 0 0 0 1 0 0 1 -4 1 0 0 1 0
0 0 0 0 0 0 0 1 0 0 1 -4 0 0 0 1

0 0 0 0 0 0 0 0 1 0 0 0 -4 1 0 0
0 0 0 0 0 0 0 0 0 1 0 0 1 -4 1 0
0 0 0 0 0 0 0 0 0 0 1 0 0 1 -4 1
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 -4

Surprising that you really need the explicit form of this matrix, though. Usually you see this kind of object handled more indirectly via sparse matrix techniques.



Related Topics



Leave a reply



Submit