Hi Ben,
Yes it should be possible. Let me sketch how you would do it. You will need to fill in a meaningful model to have a working example, so I haven't tested this.
I'll assume in the model you have something like
for(i in 1:N)
ID[i] ~ dcat(prior_probs[1:N])
where prior_probs could be set in the constants list in nimbleModel, or it could be a variable in the model.
And I assume you are using the ID elements as stochastic indices, say like this (but I know your SCR model is different)
for(i in 1:N) {
y[i] ~ dnorm( mu[ ID[i] ], sd = 1)
You can translate your sampler to a nimble sampler that is compilable and compatible with the MCMC system like this:
ID_sampler <- nimbleFunction(
name = "ID_sampler",
contains = sampler_BASE,
setup = function(model, mvSaved, target, control) { ## Required arguments. setup is run once when the MCMC is built.
N <- length(model$y)
probs <- rep(1/N, N) ## for drawing random integers
## target should be "ID".
## The names are hard-coded below but could be generalized, but anyway target is used two lines down.
nSwap <- control$nSwap ## how many trials to do each time the sampler is run
calcNodes <- model$getDependencies(target) ## This could be broken down to individual ones, but as a simple demonstration, I'm using all together
},
run = function() {
## for uncompiled execution, you can put a browser() in here and check what is happening
for(i in 1:nSwap) {
l <- rcat(1, probs) ## We don't support compilation of sample(), so I'm using rcat instead.
nj <- which(model$y.obs[l,]>0) ## This will be a length-1 vector
possible <- which(model$z == 1)
## setdiff is not supported for compilation, so I re-arranged how to get the result you need, IIUC
njProbs <- numeric(0, length = N)
njProbs[ possible ] <- model$lamb[possible, nj]
newID <- rcat(1, njProbs)
if(model$ID[l] != newID) {
model$y.true[model$ID[l],] <<- model$y.true[model$ID[l],]- model$y.obs[l,] #subtract this observation from current ID
model$y.true[model$newID,] <<- model$y.true[newID,]+model$y.obs[l,] #add this observation to new ID
model$ID[l]=newID
}
}
model$calculate(calcNodes) ## This updates the log probabilities in the model
nimCopy(model, mvSaved, nodes = calcNodes, row = 1, logProb = TRUE) ## This updates the saved states of the model
}
)
Once you have used "model <- nimbleModel(...)", you would do as follows
MCMCconf <- configureMCMC(model)
MCMCconf$printSamplers() ## inspect default samplers
MCMCconf$removeSamplers("ID") ## remove default samplers for ID
MCMCconf$addSampler(type = ID_sampler, target = "ID", control = list(nSwap = 10)) ## add your new sampler
MCMCconf$printSamplers() ## inspect modified samplers
MCMC <- buildMCMC(MCMCconf) ## build the MCMC from the configuration
MCMCrun(niter = 10) ## debug by running it uncompiled
## When you are happy with it, you can skip uncompiled testing and compile and run
Cmodel <- compileNimble(model)
CMCMC <- compileNimble(MCMC, project = model) ## the compiled MCMC will use the compiled model
CMCMC$run(niter)
I don't immediately see why a Gibbs update with no model probability calculations makes sense, but that's for you to be clear on.
If you get it working, if nSwap is small, it could be more efficient to calculate and copy only the swapped nodes and their dependencies, but I've used all nodes for now.
HTH
-Perry