# HEADER ####################################################
# This is file spam/tests/jss_areal_counts.R.               #
# It is part of the R package spam,                         #
#  --> https://CRAN.R-project.org/package=spam              #
#  --> https://CRAN.R-project.org/package=spam64            #
#  --> https://git.math.uzh.ch/reinhard.furrer/spam         #
# by Reinhard Furrer [aut, cre], Florian Gerber [aut],      #
#    Roman Flury [aut], Daniel Gerber [ctb],                #
#    Kaspar Moesinger [ctb]                                 #
# HEADER END ################################################


# JSS article: 
#     "Pitfalls in the implementation of Bayesian
#      hierarchical modeling of areal count data.
#      An illustration using BYM and Leroux models."
#
# test the MCMC sampler from the paper with 30 iterations.


# SETUP:
library("spam")
options(spam.structurebased=TRUE)

# BYM ---------------------------------------------
data(Oral); attach(Oral) 
path <- system.file("demodata/germany.adjacency", package = "spam")
A <- adjacency.landkreis(path); n <- dim(A)[1]

set.seed(2)
hyperA <- c(1, 1); hyperB <- c(0.5, .01)  
totalg <- 30

upost <- vpost <- array(0, c(totalg, n))  
kpost <- array(NA, c(totalg, 2)); accept <- rep(NA, totalg)
upost[1,] <- vpost[1,] <- rep(.001, 544); kpost[1,] <- c(10, 100) 

eta <- upost[1,] + vpost[1,]
C <- exp(eta) * E; diagC <- diag.spam(c(rep(0, n), C))
b <- c( rep(0, n), Y + (eta - 1) * C)
Qu <- R <- precmat.IGMRFirreglat(A); pad(Qu) <- c(2 * n, 2 * n)
Qv <- as.spam(rbind(cbind( diag(n), -diag(n)),
                      cbind(-diag(n),  diag(n))))
Q <- kpost[1,1] * Qu + kpost[1,2] * Qv + diagC
struct <- chol(Q, memory = list(nnzcolindices = 6467))
uRuHalf <- t(upost[1,]) %*% (R %*% upost[1,]) / 2
vvHalf <- t(vpost[1,]) %*% vpost[1,] / 2
postshape <- hyperA + c(n - 1, n) / 2

for (i in 2:totalg) {
     kpost[i,] <- rgamma(2, postshape, hyperB + c(uRuHalf, vvHalf))
 
     etaTilde <- eta
     for(index in 1:2){
         C <- E * exp(etaTilde)
         diagC <- diag.spam(c(rep(0, n), C))
         b <- c(rep(0, 544), Y + (etaTilde - 1) * C)
         Q <- kpost[i,1] * Qu + kpost[i,2] * Qv + diagC
         etaTilde <- c(solve.spam(Q, b,
                                  Rstruct = struct))[1:n + n]
     }
 
     C <- exp(etaTilde) * E; diagC <- diag.spam(c(rep(0, n), C))
     b <- c( rep(0, n), Y + (etaTilde - 1) * C)
     Q <- kpost[i,1] * Qu + kpost[i,2] * Qv + diagC

     x_ <- c(rmvnorm.canonical(1, b, Q, Rstruct = struct))
     upost[i,] <- x_[1:n]; eta_ <- x_[1:n + n]; vpost[i,] <- eta_ - upost[i,]
     uRuHalf_ <- t(upost[i,]) %*% (R %*% upost[i,]) / 2
     vvHalf_ <- t(vpost[i,]) %*% vpost[i,] / 2

     etaTilde_ <- eta_
     for(index in 1:2){
       C_ <- E * exp(etaTilde_)
       diagC_ <- diag.spam(c(rep(0, n), C_))
       b_ <- c(rep(0, 544), Y + (etaTilde_ - 1) * C_)
       Q_<- kpost[i,1] * Qu + kpost[i,2] * Qv + diagC_
       etaTilde_ <- c(solve.spam(Q_, b_,
                                Rstruct = struct))[1:n + n]
     }
     
     C_ <- exp(etaTilde_) * E; diagC_ <- diag.spam(c(rep(0, n), C_))
     b_ <- c( rep(0, n), Y + (etaTilde_ - 1) * C_)
     Q_ <- kpost[i,1] * Qu + kpost[i,2] * Qv + diagC_

     logPost_ <- sum(Y * eta_ - E * exp(eta_)) -
         kpost[i,1] * uRuHalf_ - kpost[i, 2] * vvHalf_
     logPost  <- sum(Y * eta - E * exp(eta)) - kpost[i,1] * uRuHalf -
         kpost[i,2] * vvHalf
     logApproxX_ <- - kpost[i,1] * uRuHalf_ - kpost[i,2] * vvHalf_ -
         sum(.5 * eta_^2 * C) + sum(b * eta_)
     logApproxX  <- - kpost[i,1] * uRuHalf  - kpost[i,2] * vvHalf -
         sum(.5 * eta^2 * C_) + sum(b_ * eta)
     logAlpha <- min(0, logPost_ - logPost + logApproxX - logApproxX_)
 
     if (log(runif(1)) < logAlpha) {    
         uRuHalf <- uRuHalf_;  vvHalf <- vvHalf_
         eta <- eta_; b <- b_; C <- C_; accept[i] <- 1
     } else{                            
       accept[i] <- 0; upost[i,] <- upost[i-1,]; vpost[i,] <- vpost[i-1,]} 
 }
 
# values of  30th iteration
head(eta)
tail(b)
head(C)
tail(accept)
tail(upost[30,])
tail(vpost[30,])
sum(accept[-1])
sum(upost)

