Hi David,
Thanks so much for taking the time to send this. I took some time to look through it, but eventually had to conclude it was beyond my fairly rudimentary understanding of statistics! I therefore thought I would start with what seemed like a more basic solution to achieve integer simulation values in order to use a binomial distribution to map cases to infections. My theory was that I could just swap the ODE solver to a discrete tau-leaping algorithm (I went for the package adaptivetau). However, while I am able to simulate data successfully using the ssa.adaptivetau function, when I use it as a NimbleFunction, I start getting input errors (despite the fact that the inputs should be exactly the same?) I was wondering if you could think of any fundamental reasons why this approach wouldn't work (both technically in Nimble and mathematically)? The problem appears to be with the rate function, which is used as an argument within the NimbleFunction. I think maybe I need to declare it as an argument within the nimbleRcall() function, but I'm not sure if that's possible? Can you think of an alternative solution? (Hopefully this makes sense with the code!). As always, your input is much appreciated!
Here is the code:
library(nimble)
library(dplyr)
library(adaptivetau)
##################################### FUNCTIONS #################################################
#Tau-leap rate function
rate_function <- function(x, p, t) {
return(c(x["I"]*p$e, #Bacteria Birth
0.33, #Bacteria death
p$mu * (x["S"] + x["I"]), # Human birth
p$mu * x["S"], #Susceptible death
p$a * (x["B"] / (x["B"] + 10**6)) * x["S"], # Infection
p$r * x["I"])) #Infective death
}
#Convert from who-knows-what timescale to daily
get_daily <- function(results,num_days){
df <- as.data.frame(results)
df$day <- ceiling(df$time)
daily <- data.frame(day = 1:num_days, S = NA, I = NA, B = NA)
for(i in 1:num_days){
day <- df %>%
filter(day == i)
if(nrow(day) > 0){
daily[i,2:4] <- tail(day[,2:4],1)
} else {
daily[i,2:4] <- daily[i-1,2:4]
}
}
return(daily)
}
rate_function <- function(x, p, t) {
return(c(x["I"]*p$e, #Bacteria Birth
0.33, #Bacteria death
p$mu * (x["S"] + x["I"]), # Human birth
p$mu * x["S"], #Susceptible death
p$a * (x["B"] / (x["B"] + 10**6)) * x["S"], # Infection
p$r * x["I"])) #Infective death
}
##Simulate Data
SIB_daily <- function(z_init, theta){
#Transition matrix
transitions = ssa.maketrans(names(z_init),
rbind('B', +1), # Bacteria birth
rbind('B', -1), # Bacteria death
rbind('S', +1), # Human birth
rbind('S', -1), #Susceptible death
rbind('S', -1, 'I', +1), #Infection
rbind('I', -1) #Infective death
)
num_days = 300
theta_list <- as.list(theta)
results =ssa.adaptivetau(z_init, transitions, rate_function, theta_list, tf= num_days)
daily = get_daily(results,num_days)
return(daily)
}
#Remove intial values and day column for Nimble
SIB_daily_nim <- function(z_init, theta){
SIB_daily_nim <- SIB_daily(z_init,theta)[-1,2:4]
return(as.matrix(SIB_daily_nim))
}
nimODE <- nimbleRcall(function(z_init = integer(1), theta = double(1)){}, Rfun = 'SIB_daily_nim',
returnType = integer(2))
##################################### INITIAL SET UP ###########################################
#Parameters
theta <- c(
a = 1,
e = 10,
r = 0.2,
mu = 0.0001
)
#Set initial values
N = 10000
I0 = 10
B0 = 0
S0 = N - I0
num_days = 300
z_init <- c(S = S0, I = I0, B = B0)
#Transitions
SIB_daily_sim <- SIB_daily(z_init, theta) #Simulate 'true' values
daily_cases <- rbinom(n = nrow(SIB_daily_sim), size = SIB_daily_sim$I, p = 0.3)[-1] #Simulate reported cases
###################################### NIMBLE MODEL #################################################
Model <- nimbleCode({
for(i in 1:n){
y[i] ~ dbin(prob = p, size = z[i,2])
}
z[1:n,1:3] <- nimODE(z_init=z_init[1:3],theta=theta[1:4])
## Priors
p ~ dbeta(shape1 = 1.3, shape2 = 5) #P
theta[1] ~ T(dnorm(0,5),0,) #dgamma(3,0.6) #a
theta[2] ~ T(dnorm(0,5),0,) #dgamma(10,1) #e
theta[3] ~ T(dnorm(0,5),0,) #dgamma(3, 0.06) #r
theta[4] ~ T(dnorm(0,0.1),0,)
})
Consts <- list(n = num_days - 1)
nimbleData <- list(y=daily_cases,z_init=z_init)
inits <- list(theta=c(1,1,1,0.001),p=0.3)
nimbleModel <- nimbleModel(code=Model, name='nimbleModel', constants = Consts, data = nimbleData,inits = inits)
MCMCconfig <- configureMCMC(nimbleModel,monitors=c("theta","p"))