[R] Vectorized code for generating the Kac (Clement) matrix
Berend Hasselman
bhh at xs4all.nl
Fri Apr 26 16:08:38 CEST 2013
On 26-04-2013, at 14:42, Enrico Schumann <es at enricoschumann.net> wrote:
> On Thu, 25 Apr 2013, Ravi Varadhan <ravi.varadhan at jhu.edu> writes:
>
>> Hi, I am generating large Kac matrices (also known as Clement matrix).
>> This a tridiagonal matrix. I was wondering whether there is a
>> vectorized solution that avoids the `for' loops to the following code:
>>
>> n <- 1000
>>
>> Kacmat <- matrix(0, n+1, n+1)
>>
>> for (i in 1:n) Kacmat[i, i+1] <- n - i + 1
>>
>> for (i in 2:(n+1)) Kacmat[i, i-1] <- i-1
>>
>> The above code is fast, but I am curious about vectorized ways to do this.
>>
>> Thanks in advance.
>> Best,
>> Ravi
>>
>
>
> This may be a bit faster; but as Berend and you said, the original
> function seems already fast.
>
> n <- 5000
>
> f1 <- function(n) {
> Kacmat <- matrix(0, n+1, n+1)
> for (i in 1:n) Kacmat[i, i+1] <- n - i + 1
> for (i in 2:(n+1)) Kacmat[i, i-1] <- i-1
> Kacmat
> }
> f3 <- function(n) {
> n1 <- n + 1L
> res <- numeric(n1 * n1)
> dim(res) <- c(n1, n1)
> bw <- n:1L ## bw = backward, fw = forward
> fw <- seq_len(n)
> res[cbind(fw, fw + 1L)] <- bw
> res[cbind(fw + 1L, fw)] <- fw
> res
> }
>
> system.time(K1 <- f1(n))
> system.time(K3 <- f3(n))
> identical(K3, K1)
>
> ## user system elapsed
> ## 0.132 0.028 0.161
> ##
> ## user system elapsed
> ## 0.024 0.048 0.071
> ##
Using some of your code in my function I was able to speed up my function f2.
Complete code:
f1 <- function(n) { #Ravi
Kacmat <- matrix(0, n+1, n+1)
for (i in 1:n) Kacmat[i, i+1] <- n - i + 1
for (i in 1:n) Kacmat[i+1, i] <- i
Kacmat
}
f2 <- function(n) { # Berend 1 modified to use 1L
Kacmat <- matrix(0, n+1, n+1)
Kacmat[row(Kacmat)==col(Kacmat)-1L] <- n:1L
Kacmat[row(Kacmat)==col(Kacmat)+1L] <- 1L:n
Kacmat
}
f3 <- function(n) { # Enrico
n1 <- n + 1L
res <- numeric(n1 * n1)
dim(res) <- c(n1, n1)
bw <- n:1L ## bw = backward, fw = forward
fw <- seq_len(n)
res[cbind(fw, fw + 1L)] <- bw
res[cbind(fw + 1L, fw)] <- fw
res
}
f4 <- function(n) {# Berend 2 using which with arr.ind=TRUE
Kacmat <- matrix(0, n+1, n+1)
k1 <- which(row(Kacmat)==col(Kacmat)-1L, arr.ind=TRUE)
k2 <- which(row(Kacmat)==col(Kacmat)+1L, arr.ind=TRUE)
Kacmat[k1] <- n:1L
Kacmat[k2] <- 1L:n
Kacmat
}
library(compiler)
f1.c <- cmpfun(f1)
f2.c <- cmpfun(f2)
f3.c <- cmpfun(f3)
f4.c <- cmpfun(f4)
f1(n)
f2(n)
n <- 5000
system.time(K1 <- f1(n))
system.time(K2 <- f2(n))
system.time(K3 <- f3(n))
system.time(K4 <- f4(n))
system.time(K1c <- f1.c(n))
system.time(K2c <- f2.c(n))
system.time(K3c <- f3.c(n))
system.time(K4c <- f4.c(n))
identical(K2,K1)
identical(K3,K1)
identical(K4,K1)
identical(K1c,K1)
identical(K2c,K2)
identical(K3c,K3)
identical(K4c,K4)
Result:
# > system.time(K1 <- f1(n))
# user system elapsed
# 0.387 0.120 0.511
# > system.time(K2 <- f2(n))
# user system elapsed
# 3.541 0.702 4.250
# > system.time(K3 <- f3(n))
# user system elapsed
# 0.108 0.089 0.199
# > system.time(K4 <- f4(n))
# user system elapsed
# 1.975 0.355 2.336
# >
# > system.time(K1c <- f1.c(n))
# user system elapsed
# 0.323 0.120 0.445
# > system.time(K2c <- f2.c(n))
# user system elapsed
# 3.374 0.422 3.807
# > system.time(K3c <- f3.c(n))
# user system elapsed
# 0.107 0.098 0.205
# > system.time(K4c <- f4.c(n))
# user system elapsed
# 1.816 0.384 2.203
# > identical(K2,K1)
# [1] TRUE
# > identical(K3,K1)
# [1] TRUE
# > identical(K4,K1)
# [1] TRUE
# > identical(K1c,K1)
# [1] TRUE
# > identical(K2c,K2)
# [1] TRUE
# > identical(K3c,K3)
# [1] TRUE
# > identical(K4c,K4)
# [1] TRUE
So Ravi's original and Enrico's versions are the quickest.
Using which with arr.ind made my version run a lot quicker.
All in all an interesting exercise.
Berend
More information about the R-help
mailing list