Accelerating quadratic form estimates

My question is another "Vector it!". A similar question appeared elsewhere ( Effective way to calculate quadratic forms: avoid loops? ), But for some reason I can't get it to work for my case.

I want to calculate the quadratic shape x'Sxfor each- pdimensional observation xin a sample size n. I could not understand a good, vectorized code, so my last option is to do this for loop. The following example toys p=2, n=100.

set.seed(123)
n <- 100
x1 <- rnorm(n)
x2 <- rnorm(n)
x <- cbind(x1,x2)
Sigma <- matrix(c(1, 2, 3, 4), ncol = 2)
z  <- rep(0, n)
for (i in 1:n) {
   z[i]  <- x[i, ] %*% solve(Sigma, x[i, ]) #quadratic form of x'S^{-1}x
}

Like many other R users who worship vectorized codes, using the for loop caused emotional pain. To alleviate the pain, I modified my code using a couple of common vectorization methods.

ap <- function(Sigma, x) apply(x, 1, function(x) x %*% solve(Sigma, x))
lap <- function(Sigma, x) unlist(lapply(1:n, function(i) x[i, ] %*% solve(Sigma, x[i, ])))
loop <- function(Sigma, x){
  z  <- rep(0, n)
  for (i in 1:n) {
    z[i]  <- x[i, ] %*% solve(Sigma, x[i, ])
  }
  z
}

, .

library(microbenchmark)
microbenchmark(lap(Sigma, x), ap(Sigma, x), loop(Sigma, x))

# Unit: milliseconds
#           expr      min       lq     mean   median       uq       max neval
#  lap(Sigma, x) 4.207434 4.444895 5.092389 4.616912 5.283504  8.440802   100
#   ap(Sigma, x) 4.360204 4.523306 5.317304 4.685396 5.412771 10.168674   100
# loop(Sigma, x) 4.518645 4.679317 6.204626 4.827831 5.438908 94.115144   100

, Rcpp, for loops?

+4
2

@konvas, @beginneR.

x'S^{-1}x, , @Ben Bolker , x'Sx. colSums(t(x) * (solve(Sigma) %*% t(x))) .

.

microbenchmark(lap(Sigma, x), 
               product = diag(x %*% solve(Sigma) %*%t(x)), 
               colsum = colSums(t(x) * (solve(Sigma) %*% t(x)))
               )
#Unit: microseconds
#          expr      min        lq      mean    median        uq      max neval
# lap(Sigma, x) 4283.616 4384.9215 4961.9761 4475.6920 4700.3885 9472.096   100
#       product  126.835  134.3315  165.3030  144.0575  199.5725  306.349   100
#        colsum   92.391  102.9265  130.0202  110.8285  146.2855  346.061   100
0

x vapply lapply,

# First, make a list of the rows of x
xl <- vector("list",nrow(x))
for (i in seq_along(xl)) xl[[i]] <- x[i, ] 

# Apply solve
solve.mat <- vapply(xl, solve, numeric(2), a = Sigma)
# Take the dot product of each pair of elements
result <- colSums(solve.mat * t(x))
all(result == lap(Sigma, x))
# [1] TRUE

library(microbenchmark)
microbenchmark(lap = lap(Sigma, x),
    csums = colSums(vapply(xl, solve, numeric(2), a = Sigma) * t(x)))
# Unit: milliseconds
#   expr      min       lq     mean   median       uq      max neval
#    lap 3.013343 3.050855 3.164558 3.097901 3.136355 4.206923   100
#  csums 2.224350 2.263772 2.354349 2.289751 2.317672 3.660294   100
+1

All Articles