Department of Applied Mathematics and Statistics
Center for Imaging Science
Johns Hopkins University
and
Paradigm, Inc.
Yale University
and
University of Maryland
and
North Carolina State University
Avanti Athreya, Michael Kane, Bryan Lewis, Zachary Lubberts, Vince Lyzinski, Youngser Park, Carey E. Priebe, Minh Tang, “Numerical tolerance for spectral decompositions of random matrices,” submitted, 2020.
We precisely quantify the impact of statistical error in the quality of a numerical approximation to a random matrix eigendecomposition, and under mild conditions, we use this to introduce an optimal numerical tolerance for residual error in spectral decompositions of random matrices. We demonstrate that terminating an eigendecomposition algorithm when the numerical error and statistical error are of the same order results in computational savings with no loss of accuracy. We also repair a flaw in a ubiquitous termination condition, one in wide employ in several computational linear algebra implementations. We illustrate the practical consequences of our stopping criterion with an analysis of simulated and real networks. Our theoretical results and real-data examples establish that the tradeoff between statistical and numerical error is of significant import for data science.
procrustes <- function(X,Y, type = "I"){
if(type == "C"){
X <- X/norm(X, type = "F")*sqrt(nrow(X))
Y <- Y*norm(X, type = "F")*sqrt(nrow(Y))
}
if(type == "D"){
tX <- rowSums(X^2)
tX[tX <= 1e-15] <- 1
tY <- rowSums(Y^2)
tY[tY <= 1e-15] <- 1
X <- X/sqrt(tX)
Y <- Y/sqrt(tY)
}
tmp <- t(X)%*%Y
tmp.svd <- svd(tmp)
W <- tmp.svd$u %*% t(tmp.svd$v)
err <- norm(X%*%W - Y, type = "F")
return(list(W=W,err=err))
}
bb <- 10
#bb <- seq(200,250,by=10)
B <- cbind( c(.5, .2,.2), c(.2,.5,.2),c(.2,.2, .5) ) / bb
m <- nrow(B)
bvec <- rep(300,m)
N <- sum(bvec); #rho <- bvec/N
rR <- rep(1:m,times=bvec)
P <- B[rR,rR]
U <- irlba(P,nu=m,nv=m,tol=1e-06)$u # default is 1e-05, use 1e-06 instead.
nmc <- 50
kvec <- seq(1,20)
err <- iter <- matrix(0,nmc,length(kvec))
for (mc in 1:nmc) {
set.seed(124+mc)
g <- sbm.game(N,B,bvec)#; summary(g)
ind <- 1
for (k in 1:length(kvec)) {
out <- irlba(g[],nu=m,nv=m,tol=2^-kvec[k])
iter[mc,ind] <- out$iter
Uhat <- out$u
err[mc,ind] <- procrustes(U,Uhat)$err
ind <- ind+1
}
}
err.mean <- colMeans(err)
err.sd <- apply(err,2,sd) / sqrt(nmc)
df1 <- data.frame(k=kvec, mean=colMeans(err), err=apply(err,2,sd)/sqrt(nmc), type="error")
df2 <- data.frame(k=kvec, mean=colMeans(iter), err=apply(iter,2,sd)/sqrt(nmc), type="iteration")
df <- rbind(df1,df2)
bi <- 3000
b <- seq(150,250,by=10)
out <- foreach (i=b) %dopar% {
B <- cbind( c(.5,.2,.2), c(.2,.5,.2),c(.2,.2,.5) ) / i
m <- nrow(B)
bvec <- rep(bi,m)
N <- sum(bvec); #rho <- bvec/N
rR <- rep(1:m,times=bvec)
P <- B[rR,rR]
U <- irlba(P,nu=m,nv=m,tol=1e-06)$u
nmc <- 50
kvec <- seq(1,20)
err <- iter <- matrix(0,nmc,length(kvec))
for (mc in 1:nmc) {
set.seed(124+mc)
g <- sbm.game(N,B,bvec)#; summary(g)
ind <- 1
for (k in 1:length(kvec)) {
out <- irlba(g[],nu=m,nv=m,tol=2^-kvec[k])
iter[mc,ind] <- out$iter
Uhat <- out$u
err[mc,ind] <- procrustes(U,Uhat)$err
ind <- ind+1
}
}
err.mean <- colMeans(err)
err.sd <- apply(err,2,sd) / sqrt(nmc)
df1 <- data.frame(k=kvec, mean=colMeans(err), err=apply(err,2,sd)/sqrt(nmc), type="err")
df2 <- data.frame(k=kvec, mean=colMeans(iter), err=apply(iter,2,sd)/sqrt(nmc), type="iter")
df <- rbind(df1,df2)
# save(df, file=paste0("~/Dropbox/Tolerance/df-n9k-b",i,".Rbin"))
}