Sums of Kronecker products of elements of one list with elements from another list in R

Viewed 117

I am not sure how to explain what I want to do in such a way that gives meaningful answers from a search in Google. For this reason, I am also not sure the best way to title this question, so my apologies if the title is unclear.

Suppose I have two lists of matrices called a and B. a is a list of column matrices, all of the same size. B is a list of square matrices, all of the same size. Each list has the same length.

> a <- list( t(t(c(1,2,3))), t(t(c(-2,1,2))) )
> a
[[1]]
     [,1]
[1,]    1
[2,]    2
[3,]    3

[[2]]
     [,1]
[1,]   -2
[2,]    1
[3,]    2

> B <- list( diag(2), diag(c(1,3)) )
> B
[[1]]
     [,1] [,2]
[1,]    1    0
[2,]    0    1

[[2]]
     [,1] [,2]
[1,]    1    0
[2,]    0    3

My goal is to produce a new list, say C, where the first element is a matrix defined by

a[[1]][1]*B[[1]] + a[[2]][1]*B[[2]], the second element is a matrix defined by

a[[1]][2]*B[[1]] + a[[2]][2]*B[[2]], and the third element is a matrix defined by

a[[1]][3]*B[[1]] + a[[2]][3]*B[[2]]. The length of the list C is equal to the length of a column matrix in a. So in this example, C should be a list of length 3.

This looks like the sum of Kronecker products (hence my title) so I have tried

kronecker(a[[1]], B[[1]]) + kronecker(a[[2]], B[[2]])
     [,1] [,2]
[1,]   -1    0
[2,]    0   -5
[3,]    3    0
[4,]    0    5
[5,]    5    0
[6,]    0    9

This gives a matrix that contains the proper numbers, but the result is not a list. I don't think this is a problem as I should be able to convert the output into the desired list. The problem is I'm not sure of how to generalize it for when the column matrices in a have a length of, say, 10 or 100.

Working on a solution I created a for-loop.

C <- list()
for (i in 1:length(a[[1]])) {
    C[[i]] = a[[1]][i]*B[[1]] + a[[2]][i]*B[[2]]
}
> C
[[1]]
     [,1] [,2]
[1,]   -1    0
[2,]    0   -5

[[2]]
     [,1] [,2]
[1,]    3    0
[2,]    0    5

[[3]]
     [,1] [,2]
[1,]    5    0
[2,]    0    9

This gives me exactly what I want for this particular example, but I'm not sure of a way to generalize this for-loop either.

Any help would be greatly appreciated.

1 Answers

The following code generalizes the kronecker products' sum and then splits the result.

The original code is

lst <- lapply(seq_along(a), function(i) kronecker(a[[i]], B[[i]]))
res <- Reduce(`+`, lst)

But following a comment of user G. Grothendieck, this can be written as

res <- Reduce("+", Map(kronecker, a, B))

Then, split the results matrix.

split.data.frame(res, rep(seq_along(a[[1]]), each = length(a)))
#$`1`
#     [,1] [,2]
#[1,]   -1    0
#[2,]    0   -5
#
#$`2`
#     [,1] [,2]
#[1,]    3    0
#[2,]    0    5
#
#$`3`
#     [,1] [,2]
#[1,]    5    0
#[2,]    0    9

And now that the temporary list lst is no longer needed,

rm(lst)    # final clean-up

As a final note, the split.data.frame method must be explicitly called. From the documentation of ?split, section Details, my emphasis:

split and split<- are generic functions with default and data.frame methods. The data frame method can also be used to split a matrix into a list of matrices, and the replacement form likewise, provided they are invoked explicitly.

If the default method is used, the matrix will be split as if it were a vector and the dim attribute will be dropped.

Related