Nothing
###########################################################################
# VariationalBayes #
# #
# The purpose of the VariationalBayes function is to estimate a model #
# with a Variational Bayes algorithm. #
###########################################################################
VariationalBayes <- function(Model, parm, Data, Covar=NULL,
Interval=1.0E-6, Iterations=1000, Method="Salimans2", Samples=1000,
sir=TRUE, Stop.Tolerance=1.0E-5, CPUs=1, Type="PSOCK")
{
########################## Initial Checks ##########################
time1 <- proc.time()
VB.call <- match.call()
if(missing(Model)) stop("Model is a required argument.")
if(!is.function(Model)) stop("Model must be a function.")
if(missing(Data)) stop("Data is a required argument.")
if(missing(parm)) {
cat("Initial values were not supplied, and\n")
cat("have been set to zero prior to VariationalBayes().\n")
parm <- rep(0, length(Data[["parm.names"]]))}
LIV <- length(as.vector(parm))
for (i in 1:length(Data)) {
if(is.matrix(Data[[i]])) {
if(all(is.finite(Data[[i]]))) {
mat.rank <- qr(Data[[i]], tol=1e-10)$rank
if(mat.rank < ncol(Data[[i]])) {
cat("WARNING: Matrix", names(Data)[[i]],
"may be rank-deficient.\n")}}}}
if({Interval <= 0} | {Interval > 1}) Interval <- 1.0E-6
Iterations <- min(max(round(Iterations), 10), 1000000)
"%!in%" <- function(x,table) return(match(x, table, nomatch=0) == 0)
if(Method %!in% c("Salimans2"))
stop("Method is unknown.")
if(Stop.Tolerance <= 0) Stop.Tolerance <- 1.0E-5
as.character.function <- function(x, ... )
{
fname <- deparse(substitute(x))
f <- match.fun(x)
out <- c(sprintf('"%s" <- ', fname), capture.output(f))
if(grepl("^[<]", tail(out,1))) out <- head(out, -1)
return(out)
}
acount <- length(grep("apply", as.character.function(Model)))
if(acount > 0) {
cat("Suggestion:", acount, " possible instance(s) of apply functions\n")
cat( "were found in the Model specification. Iteration speed will\n")
cat(" increase if apply functions are vectorized in R or coded\n")
cat(" in a faster language such as C++ via the Rcpp package.\n")}
acount <- length(grep("for", as.character.function(Model)))
if(acount > 0) {
cat("Suggestion:", acount, " possible instance(s) of for loops\n")
cat(" were found in the Model specification. Iteration speed will\n")
cat(" increase if for loops are vectorized in R or coded in a\n")
cat(" faster language such as C++ via the Rcpp package.\n")}
### Sample Size of Data
if(!is.null(Data[["n"]])) if(length(Data[["n"]]) == 1) N <- Data[["n"]]
if(!is.null(Data[["N"]])) if(length(Data[["N"]]) == 1) N <- Data[["N"]]
if(!is.null(Data[["y"]])) N <- nrow(matrix(Data[["y"]]))
if(!is.null(Data[["Y"]])) N <- nrow(matrix(Data[["Y"]]))
if(!is.null(N)) cat("Sample Size: ", N, "\n")
else stop("Sample size of Data not found in n, N, y, or Y.")
########################### Preparation ############################
m.old <- Model(parm, Data)
if(!is.list(m.old)) stop("Model must return a list.")
if(length(m.old) != 5) stop("Model must return five components.")
if(any(names(m.old) != c("LP","Dev","Monitor","yhat","parm")))
stop("Name mismatch in returned list of Model function.")
if(length(m.old[["LP"]]) > 1) stop("Multiple joint posteriors exist!")
if(!identical(length(parm), length(m.old[["parm"]])))
stop("The number of initial values and parameters differs.")
if(!is.finite(m.old[["LP"]])) {
cat("Generating initial values due to a non-finite posterior.\n")
if(!is.null(Data[["PGF"]]))
Initial.Values <- GIV(Model, Data, PGF=TRUE)
else Initial.Values <- GIV(Model, Data, PGF=FALSE)
m.old <- Model(Initial.Values, Data)
}
if(!is.finite(m.old[["LP"]])) stop("The posterior is non-finite.")
if(!is.finite(m.old[["Dev"]])) stop("The deviance is non-finite.")
parm <- m.old[["parm"]]
if(!identical(Model(m.old[["parm"]], Data)[["LP"]], m.old[["LP"]])) {
cat("WARNING: LP differs when initial values are held constant.\n")
cat(" Derivatives may be problematic if used.\n")}
##################### Begin Variational Bayes #####################
cat("Variational Bayes begins...\n")
if(Method == "Salimans2") {
VB <- .vbsalimans2(Model, parm, Data, Covar, Iterations,
Interval, Stop.Tolerance, m.old)
}
Dev <- as.vector(VB$Dev)
iter <- VB$iter
parm.len <- LIV
parm.new <- VB$parm.new
parm.old <- VB$parm.old
post <- VB$post
Step.Size <- VB$Step.Size
tol.new <- VB$tol.new
VarCov <- VB$VarCov
rm(VB)
if(iter == 1) stop("VariationalBayes stopped at iteration 1.")
if(tol.new <= Stop.Tolerance) converged <- TRUE
else converged <- FALSE
### Column names to samples
if(dim(post)[2] == length(Data[["parm.names"]]))
dimnames(post) <- list(1:dim(post)[1], Data[["parm.names"]], 1:2)
################# Sampling Importance Resampling ##################
if({sir == TRUE} & {converged == TRUE}) {
cat("Sampling from Posterior with Sampling Importance Resampling\n")
posterior <- SIR(Model, Data, mu=parm.new, Sigma=VarCov,
n=Samples, CPUs=CPUs, Type=Type)
Mon <- matrix(0, nrow(posterior), length(Data[["mon.names"]]))
dev <- rep(0, nrow(posterior))
for (i in 1:nrow(posterior)) {
mod <- Model(posterior[i,], Data)
dev[i] <- mod[["Dev"]]
Mon[i,] <- mod[["Monitor"]]
}
colnames(Mon) <- Data[["mon.names"]]}
else {
if({sir == TRUE} & {converged == FALSE})
cat("Posterior samples are not drawn due to Converge=FALSE\n")
posterior <- NA; Mon <- NA}
##################### Summary, Point-Estimate ######################
cat("Creating Summary from Point-Estimates\n")
Summ1 <- matrix(NA, parm.len, 4, dimnames=list(Data[["parm.names"]],
c("Mean","SD","LB","UB")))
Summ1[,1] <- parm.new
Summ1[,2] <- sqrt(diag(VarCov))
Summ1[,3] <- parm.new - 2*Summ1[,2]
Summ1[,4] <- parm.new + 2*Summ1[,2]
################### Summary, Posterior Samples ####################
Summ2 <- NA
if({sir == TRUE} & {converged == TRUE}) {
cat("Creating Summary from Posterior Samples\n")
Summ2 <- matrix(NA, ncol(posterior), 7,
dimnames=list(Data[["parm.names"]],
c("Mean","SD","MCSE","ESS","LB","Median","UB")))
Summ2[,1] <- colMeans(posterior)
Summ2[,2] <- sqrt(.colVars(posterior))
Summ2[,3] <- Summ2[,2] / sqrt(nrow(posterior))
Summ2[,4] <- rep(nrow(posterior), ncol(posterior))
Summ2[,5] <- apply(posterior, 2, quantile, c(0.025))
Summ2[,6] <- apply(posterior, 2, quantile, c(0.500))
Summ2[,7] <- apply(posterior, 2, quantile, c(0.975))
Deviance <- rep(0, 7)
Deviance[1] <- mean(dev)
Deviance[2] <- sd(dev)
Deviance[3] <- sd(dev) / sqrt(nrow(posterior))
Deviance[4] <- nrow(posterior)
Deviance[5] <- as.numeric(quantile(dev, probs=0.025, na.rm=TRUE))
Deviance[6] <- as.numeric(quantile(dev, probs=0.500, na.rm=TRUE))
Deviance[7] <- as.numeric(quantile(dev, probs=0.975, na.rm=TRUE))
Summ2 <- rbind(Summ2, Deviance)
for (j in 1:ncol(Mon)) {
Monitor <- rep(NA,7)
Monitor[1] <- mean(Mon[,j])
Monitor[2] <- sd(as.vector(Mon[,j]))
Monitor[3] <- sd(as.vector(Mon[,j])) / sqrt(nrow(Mon))
Monitor[4] <- nrow(Mon)
Monitor[5] <- as.numeric(quantile(Mon[,j], probs=0.025,
na.rm=TRUE))
Monitor[6] <- as.numeric(quantile(Mon[,j], probs=0.500,
na.rm=TRUE))
Monitor[7] <- as.numeric(quantile(Mon[,j], probs=0.975,
na.rm=TRUE))
Summ2 <- rbind(Summ2, Monitor)
rownames(Summ2)[nrow(Summ2)] <- Data[["mon.names"]][j]
}
}
############### Logarithm of the Marginal Likelihood ###############
LML <- list(LML=NA, VarCov=VarCov)
if({sir == TRUE} & {converged == TRUE}) {
cat("Estimating Log of the Marginal Likelihood\n")
lml <- LML(theta=posterior, LL=(dev*(-1/2)), method="NSIS")
LML[[1]] <- lml[[1]]}
else if({sir == FALSE} & {converged == TRUE}) {
cat("Estimating Log of the Marginal Likelihood\n")
LML <- LML(Model, Data, Modes=parm.new, Covar=VarCov,
method="LME")}
colnames(VarCov) <- rownames(VarCov) <- Data[["parm.names"]]
time2 <- proc.time()
############################# Output ##############################
VB <- list(Call=VB.call,
Converged=converged,
Covar=VarCov,
Deviance=as.vector(Dev),
History=post,
Initial.Values=parm,
Iterations=iter,
LML=LML[[1]],
LP.Final=as.vector(Model(parm.new, Data)[["LP"]]),
LP.Initial=m.old[["LP"]],
Minutes=round(as.vector(time2[3] - time1[3]) / 60, 2),
Monitor=Mon,
Posterior=posterior,
Step.Size.Final=Step.Size,
Step.Size.Initial=Step.Size,
Summary1=Summ1,
Summary2=Summ2,
Tolerance.Final=tol.new,
Tolerance.Stop=Stop.Tolerance)
class(VB) <- "vb"
cat("Variational Bayes is finished.\n\n")
return(VB)
}
.vbsalimans2 <- function(Model, parm, Data, Covar, Iterations, Interval,
Stop.Tolerance, m.old)
{
m.new <- m.old
LIV <- length(parm)
m <- parm
if(is.null(Covar)) V <- diag(LIV) #Variance
else {
V <- as.positive.definite(Covar)
if(nrow(V) != ncol(V)) stop("Covar is not square.")
if(nrow(V) != LIV) V <- diag(LIV)
else {
V <- Covar
diag(V) <- abs(diag(V))}}
rm(Covar)
z <- m #Guess of the mean
P <- as.inverse(V) #Precision
a <- rep(0, LIV)
zbar <- rep(0, LIV)
Pbar <- matrix(0, LIV, LIV)
abar <- rep(0, LIV)
w <- 1 / sqrt(Iterations) #step-size
half1 <- Iterations / 2
half2 <- 2 / Iterations
post <- array(0, dim=c(Iterations, LIV, 2))
post[1,,1] <- m
post[1,,2] <- diag(V)
Dev <- matrix(m.old[["Dev"]],1,1)
### Stochastic Approximation
for (iter in 1:Iterations) {
mbar <- mbar.last <- m #mean
Vbar <- Vbar.last <- V #variance
m.old <- m.new #model
### Print Status
if(iter %% round(Iterations / 10) == 0) {
cat("Iteration: ", iter, " of ", Iterations, "\n")}
### Draw a sample from the approximating distribution q
xstar <- m
while(identical(xstar, m)) {
xstar <- try(m +
as.vector(matrix(rnorm(LIV),1,LIV) %*% chol(V)),
silent=TRUE)
if(inherits(xstar, "try-error"))
xstar <- rnorm(LIV,m,abs(diag(V)))
m.temp <- try(Model(xstar, Data), silent=TRUE)
if(inherits(xstar, "try-error")) xstar <- m
else xstar <- m.temp[["parm"]]
if(any(!is.finite(c(m.temp[["LP"]], m.temp[["Dev"]],
m.temp[["Monitor"]]))))
xstar <- m}
### Gradient and Hessian
g <- partial(Model, xstar, Data, Interval=Interval)
H <- Hessian(Model, xstar, Data, Interval=Interval)
### Stochastic Approx.
a <- (1 - w)*a + w*g
P <- (1 - w)*P - w*H
z <- (1 - w)*z + w*xstar
if(any(!is.finite(P))) P <- diag(LIV)
if(!is.symmetric.matrix(P))
P <- as.symmetric.matrix(P)
diag(P) <- abs(diag(P))
if(!is.positive.definite(P))
P <- as.positive.definite(P)
V <- as.inverse(P)
m <- as.vector(V %*% a + z)
### Evaluate Proposal
m.new <- try(Model(m, Data), silent=TRUE)
if(inherits(m.new, "try-error")) m.new <- m.old
else if(any(!is.finite(c(m.new[["LP"]], m.new[["Dev"]],
m.new[["Monitor"]]))))
m.new <- m.old
else if(log(runif(1)) >= (m.new[["LP"]] - m.old[["LP"]]))
m.new <- m.old
m <- m.new[["parm"]]
### Storage
post[iter,,1] <- m
post[iter,,2] <- diag(V)
Dev <- rbind(Dev, m.new[["Dev"]])
### Do averaging if over half-way
if(iter > half1) {
abar <- abar + half2*g
Pbar <- Pbar - half2*H
zbar <- zbar + half2*xstar
if(any(!is.finite(Pbar))) Pbar <- diag(LIV)
if(!is.symmetric.matrix(Pbar))
Pbar <- as.symmetric.matrix(Pbar)
diag(Pbar) <- abs(diag(Pbar))
if(!is.positive.definite(Pbar))
Pbar <- as.positive.definite(Pbar)
Vbar <- as.inverse(Pbar)
mbar <- as.vector(Vbar %*% abar + zbar)
mbar <- Model(mbar, Data)[["parm"]]
### Tolerance
tol.new <- sum(sqrt(sum({mbar - mbar.last} *
{mbar - mbar.last})),
sqrt(sum({diag(Vbar) - diag(Vbar.last)} *
{diag(Vbar) - diag(Vbar.last)})))
if(tol.new <= Stop.Tolerance) {
post <- post[1:iter,,]
break}
}
}
Dev <- Dev[-1,]
#mbar and Vbar should be returned, but not if wildly different...
LB <- mbar - 3*diag(Vbar)
UB <- mbar + 3*diag(Vbar)
if(any((m < LB) | (m > UB))) {
mbar <- m
Vbar <- diag(LIV) * diag(V)}
### Output
VB <- list(Dev=Dev, iter=iter, parm.new=mbar, parm.old=parm,
post=post, Step.Size=w, tol.new=tol.new, VarCov=Vbar)
return(VB)
}
#End
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.