I am trying to utilize the latent variable framework of Chib (1993) to exploit conjugacy for a probit regression. The basic idea of this latent variable representation is that if y ~ Bern(Phi(x’b)) then we can write
Pr(y = 1 | x, b) = \int Pr(y = 1 | y*, x, b) f(y* | x , b) dy*
where Pr(y = 1 | y*, x, b) = 1 if y* > 0 and 0 otherwise (i.e., y is deterministic given y*).
More importantly, y* | y = 1 ~ TN(x’b, 1, a = 0, b = Inf) and y* | y = 0 ~ TN(x’b, 1, a = -Inf, b = 0), which is easy to sample from.
The Nimble code I have is as follows:
code <- nimbleCode({
for ( i in 1:n ) {
mean[i] <- beta0 + beta1 * x[i]
ystar[i] ~ dnorm(mean[i], sd = 1)
y[i] ~ dinterval(ystar[i], 0)
}
beta0 ~ dnorm(0, sd = 10)
beta1 ~ dnorm(0, sd = 10)
})
While this appears to work in terms of getting an answer close to the MLE, the sampler does not exploit the conjugacy of ystar (although, interestingly, it does use a conjugate sampler for beta).
I have attempted to write the quantile and random number generator function as follows:
qlatentnorm = nimbleFunction(
run = function(
p = double(0), mean = double(0, default = 0), y = integer(0)
, lower.tail = integer(0, default = 1)
, log.p = integer(0, default = 0)
) {
returnType(double(0))
if (log.p)
p <- exp(p)
if (!lower.tail)
p <- 1 - p
if ( y == 0 ) {
logp <- log(p) + pnorm(0, mean = mean, log.p = 1)
return( qnorm(logp, mean = mean, log.p = 1) )
} else {
lowProb <- pnorm(0, mean = mean)
return( qnorm( p + lowProb * (1 - p), mean = mean ) )
}
return(0) ## never reached
}
)
rlatentnorm = nimbleFunction(
run = function(
n = integer(0), mean = double(0, default = 0), y = integer(0)
) {
returnType(double(0))
return( qlatentnorm(runif(1), mean = mean, y = y) )
}
)
Both of these functions compile fine, and a histogram vs. density plot suggests that these functions have been implemented correctly.
I have programmed sampling the y*’s as follows:
latentNormalSampler <- nimbleFunction(
name = 'latentNormalSampler',
contains = sampler_BASE,
setup = function(model, mvSaved, target, control) {
## Get mean parameters
means <- model$getParents(target)
## Get corresponding binary variables
ybin <- model$getDependencies(target, stochOnly = TRUE, self = FALSE)
## Get number of latent variables to update
nupdates <- length(ybin)
}
, run = function() {
## Empty vector for draws
draw <- rep(0, nupdates)
for ( i in 1:nupdates ) {
mean_i <- values(model, means[i])[1]
ybin_i <- values(model, ybin[i])[1]
draw[i] <- rlatentnorm(1, mean_i, ybin_i)
}
model[[target]] <<- draw
},
methods = list(
reset = function() { }
)
)
While this is lightning fast, it produces results that do not align at all with the MLE.
--
You received this message because you are subscribed to the Google Groups "nimble-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nimble-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/nimble-users/64742c0c-deb9-4e89-877b-64d582141a99n%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/nimble-users/7a27de0c-aa2c-4c1f-b6b8-59639ce0a631n%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/nimble-users/CAKbe0hrVxEC%2BDTfJD1%3Dt9JF286bhtC%2BKQPTavTK6jq%3D%3D4L3MgQ%40mail.gmail.com.