But I'm not the only one with my hands on the steering wheel of the
Stan bus. And I'm more concerned that the lower-level functionality is
implemented cleanly than the high-level calls (for instance, "stan()" calls
compilers, data readers, and then one or more chain runners --- it's
the thing stan() calls that I'm most concerned with).
this$get_J()We're about to undertake a major overhaul on the RStan interface,
so now is the time to get feature requests in!
I think everyone should temper their expectations until(a) we limit Stan models to DAGs or (b) we find somemagical algorithm that can compute marginal distributionsin closed form.
I don't see anything you're missing, so maybe Michael
will chime in.
I don't understand why Michael's talking about computing marginal
distributions in closed form --- we just have Bayes's rule and compute
everything up to a constant. I think that'll work for your
proposal.
If we did limit models to DAGs a la BUGS, then there's more
that we could infer from the graph structure, such as Markov
blankets, which would make doing a proper per-variable
Gibbs easy to implement. The fact that we don't do that is
why we don't have any discrete sampling.
And just to be clear about our goals, we're not planning on building these
Gibbs updates any time soon. And if you had to implement full Gibbs this way,
as in trying to code up BUGS in Stan, it'd be too much copying to be efficient
unless there were only a few blocks.
library(Rux)
library(MASS)
library(rstan)
## From total marked population size (N), times of surveys (tos),
## survival probability (phi), and recapture probability (p),
## simulate times of ``births'', followed by the survival process
## (time of alive matrix, toaM), and the recapture process
## (time of recpature matrix, torM). From these, calculate the
## list of times of recapture (tor) and the vector of times of
## death (tod).
tos <- 1:10
N <- 200
phi <- rep(.8,max(tos))
p <- 0.7
tbs <- rbinom(n=N, size=5, prob=.3)+1
toaM <- matrix(0,nrow=N, ncol=length(tos))
torM <- matrix(0,nrow=N, ncol=length(tos))
survive <- 0
die <- 0
recapture <- 0
miss <- 0
for ( i in 1:N ) {
toaM[i,tbs[i]] <- 1
torM[i,tbs[i]] <- 1
for (j in (tbs[i]+1):max(tos) ) {
toaM[i,j] <- toaM[i,j-1] *
sample(x=c(0,1),size=1,replace=TRUE, prob=c(1-phi[j], phi[j]))
if ((toaM[i,j] == 1) && (toaM[i,j-1] == 1)) survive <- survive + 1
if ((toaM[i,j] == 0) && (toaM[i,j-1] == 1)) die <- die + 1
torM[i,j] <- toaM[i,j] *
sample(x=c(0,1),size=1,replace=TRUE, prob=c(1-p, p))
if ((toaM[i,j] == 1) && (torM[i,j] == 1)) recapture <- recapture + 1
if ((toaM[i,j] == 1) && (torM[i,j] == 0)) miss <- miss + 1
}
}
tor <- apply(X=torM, MARGIN=1, FUN=function(x) {which(x==1)})
tod <- apply(X=toaM, MARGIN=1, FUN=function(x) {max(which(x==1))+1})
## Calculate realized survival and recapture probabilities.
true_phi <- survive/(survive + die)
true_p <- recapture/(recapture + miss)
## Create matrices of (bad) initial values for the parameters of the
## survival (PHI) and recapture (P) processes. These matrices are
## N x max(tos)*5 by default.
P <- matrix(data=0.6, nrow=N, ncol=max(tos)*5)
P[,(max(tos)+1):(max(tos)*5)] <- 0
PHI = 0.5
## Create a handle to the likelihood calculation and discrete slice
## sampler for time of death state.
o <- new("recapture_handle",
times_of_surveys = tos,
times_of_recaptures = tor,
times_of_deaths = tod,
known_deaths = rep(FALSE, N),
PHI = PHI,
P = P
)
## Calculate the initial likelihood, the time taken for this calculation
## should scale with N * K * 5.
ll_calculation_time <- system.time(expr=o$log_likelihood)
## Create vectors to hold output from MCMC.
N_it <- 1000
o_accept <- vector(mode="numeric", length=N_it)
o_PHI <- vector(mode="numeric", length=N_it)
o_P <- vector(mode="numeric", length=N_it)
o_lpd <- vector(mode="numeric", length=N_it)
## Stan model for simulating from PHI/P conditional on known
## birth, death, and recapture times for n tags.
stan_code <- '
data {
int<lower=0> n_tags;
int<lower=0> n_occasions;
int<lower=0> tb[n_tags];
int<lower=0> td[n_tags];
int<lower=0, upper=1> recaptures[n_tags, n_occasions];
}
// Declare parameters for the linear models.
parameters {
real beta;
real gamma;
}
// Rubber meets the road for linear model specification.
transformed parameters {
real PHI_logit[n_tags, 5*n_occasions];
real P_logit[n_tags, 5*n_occasions];
for ( tag in 1:n_tags ) { // indexing covers PHI/P
for ( t in tb[tag]:(td[tag]-1) ) {
PHI_logit[tag,t] <- beta;
P_logit[tag,t] <- gamma;
}
}
}
model {
// Priors for linear model coefficients.
beta ~ cauchy(0,2.5);
gamma ~ cauchy(0,2.5);
// Mark-recapture likelihood.
for ( tag in 1:n_tags ) {
for ( t in tb[tag]:(td[tag]-2) ) { // SURVIVES
1 ~ bernoulli_logit(PHI_logit[tag,t]);
}
0 ~ bernoulli_logit(PHI_logit[tag,td[tag]-1]); // DIES
for ( t in (tb[tag]+1):min(n_occasions,td[tag]-1) ) { // AVAILABLE
recaptures[tag,t] ~ bernoulli_logit(P_logit[tag,t]);
}
}
}
'
n_tags <- dim(o$recaptures)[1]
n_occ <- dim(o$recaptures)[2]
stan_fit <- NULL
stan_iter <- 200
stan_timing <- system.time({
for ( it in 1:N_it ) {
print(it)
## Sample state:
tds <- c(o$set_deaths(o$td))
## Sample PHI/P matrices:
stan_fit <- stan(
model_code=stan_code,
data=list(
n_tags = n_tags,
n_occasions = n_occ,
tb = tbs,
td = tds,
recaptures = o$recaptures
), fit=stan_fit,
iter=stan_iter, chains=1, warmup=stan_iter/2,
pars=c('beta','gamma')
)
## Make matrices and re-assign them:
stan_samples <- extract(stan_fit, permuted=TRUE)
PHImat <- matrix(
data=plogis(stan_samples$beta[stan_iter/2]),
nrow=o$N, ncol=o$K*5, byrow=TRUE)
Pmat <- matrix(
data=plogis(stan_samples$gamma[stan_iter/2]),
nrow=o$N, ncol=o$K*5, byrow=TRUE)
Pmat[,(o$K+1):ncol(Pmat)] <- 0
o$PHI <- PHImat
o$P <- Pmat
## Record for posterity
o_PHI[it] = unique(c(o$PHI))
o_P[it] = max(unique(c(o$P)))
o_lpd[it] = o$log_likelihood
}
})
plot(o_PHI, pch='*')
plot(o_lpd, pch='*')
plot(o_PHI, o_lpd, pch = '*')
plot(o_PHI, o_P, pch = '*')
abline(h=recapture/(recapture+miss))
abline(v=survive/(survive+die))
Can you guys clarify something for me? Are we trying to expose thefull conditional density,p(alpha | beta, x)or just something proportional to it,~ p(alpha | beta, x) ~ p(alpha, beta) | beta=const?The former is what’s hard and the cause of my concern.
If it’s justthe latter that’s necessary then it’s no problem. Apologies if I wasmistaken.
The two steps are: a) simulate alpha from something proportional to p(alpha|beta,x),
let's say Stan can do this; and b) simulate beta from something proportional to
p(beta|alpha,x), let's say something outside of Stan (say R, Python) can do this.
I don't think we ever need the full conditional, just something proportional.
I don't think the density proportional to p(alpha|beta,x) ever needs to be exposed from
Stan for this scheme to work. I just need to be able to read/write parameter/data efficiently.
It might be nice to get the density proportional to p(alpha|beta,x) but it is not necessary.
--
would be nice in order to check that the sample did fine. My version here needs the abind package.Maybe I overlooked it, but a function in rstan like this one:
check_sampler <- function(fit, ...) {
sp <- get_sampler_params(fit, inc_warmup=FALSE)
spa <- do.call(abind, c(list(along=-1), sp))
spa <- aperm(spa, c(2,1,3))
monitor(spa, warmup=0, ...)
}
monitor(as.array(fit), warmup = 0)Hi Ben!If I run as.array(fit), then I the sampler parameters are not part of the output (also the permuted argument seems broken):
R3.0.2> aa <- as.array(f$fit, permuted=FALSE)
Error in .local(object, ...) :
formal argument "permuted" matched by multiple actual arguments
> rstan:::as.array.stanfit
function (x, ...)
{
if (x@mode != 0)
return(numeric(0))
out <- extract(x, permuted = FALSE, inc_warmup = FALSE, ...)
return(out)
}
<environment: namespace:rstan>The array is correctly formatted, but monitor in check_sampler gets confused for reasons I don't get.Moreover, I noticed that monitor does not like everything I give it:eight <- stan_demo("eight_schools", chains=1)
print(eight)
check_sampler(eight)