Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding sparseMatrix for "dense" mass matrix sampler #39

Merged
Merged
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
30 changes: 24 additions & 6 deletions R/sample_tmb_deprecated.R
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,8 @@ sample_tmb_nuts <- function(iter, fn, gr, init, warmup=floor(iter/2),
npar <- length(init)
M <- control$metric
if(is.null(M)) M <- rep(1, len=npar)
if( !(is.vector(M) | is.matrix(M)) )
stop("Metric must be vector or matrix")
if( !(is.vector(M) | is.matrix(M) | is(M,"Matrix")) )
stop("Metric must be vector or matrix or Matrix")
max_td <- control$max_treedepth
adapt_delta <- control$adapt_delta
adapt_mass <- control$adapt_mass
Expand All @@ -265,6 +265,8 @@ sample_tmb_nuts <- function(iter, fn, gr, init, warmup=floor(iter/2),
fn2 <- rotation$fn2; gr2 <- rotation$gr2
theta.cur <- rotation$x.cur
chd <- rotation$chd
J <- rotation$J

sampler_params <-
matrix(numeric(0), nrow=iter, ncol=6, dimnames=list(NULL,
c("accept_stat__", "stepsize__", "treedepth__", "n_leapfrog__",
Expand Down Expand Up @@ -292,8 +294,15 @@ sample_tmb_nuts <- function(iter, fn, gr, init, warmup=floor(iter/2),
## Initialize this iteration from previous in case divergence at first
## treebuilding. If successful trajectory they are overwritten
theta.minus <- theta.plus <- theta.cur
theta.out[m,] <-
if(is.vector(M)) chd*theta.cur else t(chd %*% theta.cur)
if(is.vector(M)){
theta.out[m,] <- chd*theta.cur
}else if(is.matrix(M)){
theta.out[m,] <- t(chd %*% theta.cur)
}else if(is(M,"Matrix")){
theta.out[m,] <- t(as.numeric(J%*%solve(chd, solve(chd, J%*%theta.cur, system="Lt"), system="Pt")))
}else{
stop("M not viable")
}
lp[m] <- if(m==1) fn2(theta.cur) else lp[m-1]
r.cur <- r.plus <- r.minus <- rnorm(npar,0,1)
H0 <- .calculate.H(theta=theta.cur, r=r.cur, fn=fn2)
Expand Down Expand Up @@ -328,8 +337,17 @@ sample_tmb_nuts <- function(iter, fn, gr, init, warmup=floor(iter/2),
theta.cur <- res$theta.prime
lp[m] <- fn2(theta.cur)
## Rotate parameters
theta.out[m,] <-
if(is.vector(M)) chd*theta.cur else t(chd %*% theta.cur)
#theta.out[m,] <-
# if(is.vector(M)) chd*theta.cur else t(chd %*% theta.cur)
if(is.vector(M)){
theta.out[m,] <- chd*theta.cur
}else if(is.matrix(M)){
theta.out[m,] <- t(chd %*% theta.cur)
}else if(is(M,"Matrix")){
theta.out[m,] <- t(as.numeric(J%*%solve(chd, solve(chd, J%*%theta.cur, system="Lt"), system="Pt")))
}else{
stop("M not viable")
}
}
}
n <- n+res$n
Expand Down
34 changes: 32 additions & 2 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ check_identifiable <- function(model, path=getwd()){
## Rotation done using choleski decomposition
## First case is a dense mass matrix
if(is.matrix(M)){
J <- NULL
chd <- t(chol(M)) # lower triangular Cholesky decomp.
chd.inv <- solve(chd) # inverse
## Define rotated fn and gr functions
Expand All @@ -496,20 +497,49 @@ check_identifiable <- function(model, path=getwd()){
## Now rotate back to "x" space using the new mass matrix M
x.cur <- as.numeric(chd.inv %*% y.cur)
} else if(is.vector(M)){
J <- NULL
chd <- sqrt(M)
fn2 <- function(x) fn(chd * x)
gr2 <- function(x) as.vector(gr(chd * x) ) * chd
## Now rotate back to "x" space using the new mass matrix M. M is a
## vector here. Note the big difference in efficiency without the
## matrix operations.
x.cur <- (1/chd) * y.cur
} else if(is(M,"Matrix")){
warning( "HIGHLY EXPERIMENTAL" )
# M is actually Q, i.e., the inverse-mass
# Antidiagonal matrix JJ = I
J = Matrix::sparseMatrix( i=1:nrow(M), j=nrow(M):1 )
#chd <- Cholesky(M, super=FALSE, perm=FALSE)
#chd <- Matrix::Cholesky(M, super=TRUE, perm=FALSE)
chd <- Matrix::Cholesky(J%*%M%*%J, super=TRUE, perm=FALSE) # perm
Linv_times_x = function(chd,x){
as.numeric(J%*%solve(chd, solve(chd, J%*%x, system="Lt"), system="Pt"))
}
x_times_Linv = function(chd,x){
#x %*% chol()
as.numeric(J%*%solve(chd, solve(chd, t(x%*%J), system="L"), system="Pt"))
}
fn2 <- function(x){
Linv_x = Linv_times_x(chd, x)
fn(Linv_x)
}
gr2 <- function(x){
Linv_x = Linv_times_x(chd, x)
grad = gr( Linv_x )
as.vector( x_times_Linv(chd, grad) )
}
## Now rotate back to "x" space using the new mass matrix M
# solve(t(chol(solve(M)))) ~~ IS EQUAL TO ~~ J%*%chol(M)%*%J
# J%*%chol(J%*%prec%*%J) %*% J%*%x
x.cur <- as.numeric(J%*%chol(J%*%M%*%J) %*% J%*%y.cur)
} else {
stop("Mass matrix must be vector or matrix")
stop("Mass matrix must be vector or matrix or sparseMatrix")
}
## Redefine these functions
## Need to adjust the current parameters so the chain is
## continuous. First rotate to be in Y space.
return(list(gr2=gr2, fn2=fn2, x.cur=x.cur, chd=chd))
return(list(gr2=gr2, fn2=fn2, x.cur=x.cur, chd=chd, J=J))
}

## Update the control list.
Expand Down
Loading