Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## 2024-05-24 - Unnecessary Matrix Inversions

**Learning:** Mathematical operations that effectively cancel each other out (like computing the inverse of an inverse matrix) can significantly degrade performance in statistical libraries due to $O(K^3)$ matrix inversion complexity.

**Action:** Whenever identifying an inversion function (`chol2inv`, `solve`, etc.) being passed between functions, look for where the original matrix might already contain the solution, and pass the required state directly to avoid redundant inversions.
21 changes: 14 additions & 7 deletions R/vuongtest.R
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,12 @@ calcAB <- function(object, n, scfun, vc){
## in case mirt vcov was not estimated
if(nrow(tmpvc) == 1 & is.na(tmpvc[1,1])) stop("Please re-estimate the mirt model with SE=TRUE")
}
A <- chol2inv(chol(tmpvc))

# Bolt: performance improvement - avoid unnecessary matrix inversion
# A <- chol2inv(chol(tmpvc))
# A is the inverse of tmpvc, but later in calcLambda we compute chol2inv(chol(A))
# By saving tmpvc as Ainv we can avoid computing inverse of an inverse in calcLambda
Ainv <- tmpvc

## Eq (2.2)
if(!is.null(scfun)){
Expand All @@ -251,9 +256,9 @@ calcAB <- function(object, n, scfun, vc){
sc <- estfun(object)
}
sc.cp <- crossprod(sc)/n
B <- matrix(sc.cp, nrow(A), nrow(A))
B <- matrix(sc.cp, nrow(Ainv), nrow(Ainv))

list(A=A, B=B, sc=sc)
list(Ainv=Ainv, B=B, sc=sc)
}

## a function to get the cross-product from Eq (2.7)
Expand All @@ -271,10 +276,12 @@ calcLambda <- function(object1, object2, n, score1, score2, vc1, vc2) {
AB2 <- calcAB(object2, n, score2, vc2)
Bc <- calcBcross(AB1$sc, AB2$sc, n)

W <- cbind(rbind(-AB1$B %*% chol2inv(chol(AB1$A)),
t(Bc) %*% chol2inv(chol(AB1$A))),
rbind(-Bc %*% chol2inv(chol(AB2$A)),
AB2$B %*% chol2inv(chol(AB2$A))))
# Bolt: performance improvement - use Ainv directly instead of chol2inv(chol(AB$A))
# This eliminates matrix inversions and computing the inverse of an inverse.
W <- cbind(rbind(-AB1$B %*% AB1$Ainv,
t(Bc) %*% AB1$Ainv),
rbind(-Bc %*% AB2$Ainv,
AB2$B %*% AB2$Ainv))

lamstar <- eigen(W, only.values=TRUE)$values
## Discard imaginary part, as it only occurs for tiny eigenvalues?
Expand Down