--- title: "transition to multiple states" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{transition to multiple states} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r message=FALSE} library(deSolve) library(denim) ``` ## 1. Transition to multiple states in denim Modelers may encounter many situations where individuals can transition from one compartment to multiple others, such as the SIRD model. There are 2 main approaches to handle this: - Model transitions as competing risks - Model as multinomial transition (outgoing population is split by a fixed proportion to transition to new compartments). `denim` allows users to model both situations, while requiring minimal change to the code base to switch between 2 methods of modeling. ## 2. Multinomial in denim ### 2.1. Model definition To specify the proportion that goes to each outgoing compartment, define the transition using the following syntax in model definition: `"proportion * compartment -> out_compartment" = [transition]` Note that the `proportion` here is the proportion of `compartment` that will end up in `out_compartment` [at equilibrium]{.underline}. ***Example:*** An SIRD model where 90% of infected can recover while the remaining 10% will die. ```{r} transitions <- list( "S -> I" = "beta * S * (I / N) * timeStep", "0.9 * I -> R" = d_gamma(1/3, 2), "0.1 * I -> D" = d_exponential(0.1) ) ``` ```{=html}
Mathematical formulation for the model ``` $$ \begin{cases} dS = -\beta S \frac{I}{N} \\ dIR_1 = 0.9 *\beta S \frac{I}{N} -\frac{1}{3}IR_1 \\ dIR_2 = \frac{1}{3}IR_1 - \frac{1}{3}IR_2 \\ dID = 0.1*\beta S \frac{I}{N} - 0.1*ID \\ dR = \frac{1}{3}IR_2 \\ dD = 0.1*ID \end{cases} $$
#### Proportion normalization `denim` will automatically normalize the proportions if they don't sum up to 1. ***Example 2:*** The following model definition is equivalent to the one in ***Example 1*** ```{r} transitions <- list( "S -> I" = "beta * S * (I / N) * timeStep", "36 * I -> R" = d_gamma(1/3, 2), "4 * I -> D" = d_exponential(0.1) ) ``` ### 2.2. Example model To further demonstrate the implementation of multinomial in `denim`, we provide the equivalent model implemented in `deSolve` and compare the output of 2 implementations. #### Model definition in denim ```{r} # model in denim transitions <- list( "S -> I" = "beta * S * (I / N) * timeStep", "0.9 * I -> R" = d_gamma(1/3, 2), "0.1 * I -> D" = d_exponential(0.1) ) denimInitialValues <- c( S = 999, I = 1, R = 0, D = 0 ) ``` #### Equivalent model definition in deSolve ```{r} # model in deSolve transition_func <- function(t, state, param){ with(as.list( c(state, param) ), { dS = - beta * S * (IR1 + IR2 + ID)/N # apply linear chain trick for I -> R transition # 0.9 * to specify prop of I that goes to I->R transition dIR1 = 0.9 * beta * S * (IR1 + IR2 + ID)/N - rate*IR1 dIR2 = rate*IR1 - rate*IR2 dR = rate*IR2 # handle I -> D transition # 0.1 * to specify prop of I that goes to I->D transition dID = 0.1 * beta * S * (IR1 + IR2 + ID)/N - exp_rate*ID dD = exp_rate*ID list(c(dS, dIR1, dIR2, dID, dR, dD)) }) } desolveInitialValues <- c( S = 999, # note that internally, denim also allocate initial value based on specified proportion IR1 = 0.9, IR2 = 0, ID = 0.1, R = 0, D = 0 ) ``` #### Run simulation and compare
Model settings ```{r} parameters <- c( beta = 0.2, N = 1000, rate = 1/3, exp_rate = 0.1 ) simulationDuration <- 200 timeStep <- 0.05 ```
Run model ```{r} # --- run denim model ---- mod <- sim(transitions = transitions, initialValues = denimInitialValues, parameters = parameters, simulationDuration = simulationDuration, timeStep = timeStep) # run deSolve model times <- seq(0, simulationDuration) ode_mod <- ode(y = desolveInitialValues, times = times, parms = parameters, func = transition_func) ode_mod <- as.data.frame(ode_mod) ode_mod$I<- rowSums(ode_mod[, c("IR1", "IR2", "ID")]) ```
Output Comparison ```{r echo=FALSE} # ---- Plot S compartment plot(x = mod$Time, y = mod$S,xlab = "Time", ylab = "Count", main="S compartment", col = "#4876ff", type="l", lwd=3) lines(ode_mod$time, ode_mod$S, lwd=3, lty=3) legend(x = 150, y = 850,legend=c("denim", "deSolve"), col = c("#4876ff", "black"), lty=c(1,3)) # ---- Plot I compartment plot(x = mod$Time, y = mod$I, xlab = "Time", ylab = "Count", main="I compartment", col = "#4876ff", type="l", lwd=2) lines(ode_mod$time, ode_mod$I, lwd=3, lty=3) legend(x = 150, y = 25,legend=c("denim", "deSolve"), col = c("#4876ff", "black"), lty=c(1,3)) # ---- Plot R compartment plot(x = mod$Time, y = mod$R, xlab = "Time", ylab = "Count", main="R compartment", col = "#4876ff", type="l", lwd=2) lines(ode_mod$time, ode_mod$R, lwd=3, lty=3) legend(x = 150, y = 250,legend=c("denim", "deSolve"), col = c("#4876ff", "black"), lty=c(1,3)) # ---- Plot D compartment plot(x = mod$Time, y = mod$D, xlab = "Time", ylab = "Count", main="D compartment", col = "#4876ff", type="l", lwd=2) lines(ode_mod$time, ode_mod$D, lwd=3, lty=3) legend(x = 150, y = 25,legend=c("denim", "deSolve"), col = c("#4876ff", "black"), lty=c(1,3)) ``` ## 3. Competing risks in denim ### 3.1. Model definition When there are multiple transitions from one compartment and no proportions are specified, denim will automatically treat these transitions as competing risks. ***Example:*** the following model definition will treat `I->R` and `I->D` as competing risks. ```{r} transitions <- list( "S -> I" = "beta * S * (I / N) * timeStep", "I -> R" = d_gamma(rate = 1/3, shape = 2), "I -> D" = d_gamma(rate = 1/4, shape = 2) ) ``` ### 3.2. Example model To further demonstrate the implementation of competing risks in `denim`, we provide the equivalent model implemented in `deSolve` and compare the output of 2 implementations. #### Model definition in denim ```{r} transitions <- list( "S -> I" = "beta * S * (I / N) * timeStep", "I -> R" = d_gamma(rate = 1/3, shape = 2), "I -> D" = d_gamma(rate = 1/4, shape = 2) ) denimInitialValues <- c( S = 950, I = 50, R = 0, D = 0 ) ``` #### Equivalent model definition in deSolve ```{r} transition_func <- function(t, state, param){ with(as.list( c(state, param) ), { dS = - beta * S * (I1 + I2 + IR + ID)/N dI1 = beta * S * (I1 + I2 + IR + ID)/N - (rate+d_rate)*I1 dIR = rate*I1 - (d_rate + rate)*IR dID = d_rate*I1 - (d_rate + rate)*ID dI2 = d_rate*IR + rate*ID - (d_rate + rate)*I2 dR = rate*IR + rate*I2 dD = d_rate*ID + d_rate*I2 list(c(dS, dI1, dIR, dID, dI2, dR, dD)) }) } desolveInitialValues <- c( S = 950, I1 = 50, IR = 0, ID = 0, I2 = 0, R = 0, D = 0 ) ``` #### Run simulation and compare
Model settings ```{r} parameters <- c( beta = 0.2, N = 1000, rate = 1/3, d_rate = 1/4 ) simulationDuration <- 50 timeStep <- 0.05 ```
Run model ```{r} # run denim model mod <- sim(transitions = transitions, initialValues = denimInitialValues, parameters = parameters, simulationDuration = simulationDuration, timeStep = timeStep) # run deSolve model times <- seq(0, simulationDuration) ode_mod <- ode(y = desolveInitialValues, times = times, parms = parameters, func = transition_func) ode_mod <- as.data.frame(ode_mod) ode_mod$I <- rowSums(ode_mod[,c("I1", "ID", "IR", "I2")]) ```
```{r echo=FALSE} # ---- Plot S compartment plot(x = mod$Time, y = mod$S,xlab = "Time", ylab = "Count", main="S compartment", col = "#4876ff", type="l", lwd=3) lines(ode_mod$time, ode_mod$S, lwd=3, lty=3) legend(x = 30, y = 925,legend=c("denim", "deSolve"), col = c("#4876ff", "black"), lty=c(1,3)) # ---- Plot I compartment plot(x = mod$Time, y = mod$I, xlab = "Time", ylab = "Count", main="I compartment", col = "#4876ff", type="l", lwd=2) lines(ode_mod$time, ode_mod$I, lwd=3, lty=3) legend(x = 30, y = 50,legend=c("denim", "deSolve"), col = c("#4876ff", "black"), lty=c(1,3)) # ---- Plot R compartment plot(x = mod$Time, y = mod$R, xlab = "Time", ylab = "Count", main="R compartment", col = "#4876ff", type="l", lwd=2) lines(ode_mod$time, ode_mod$R, lwd=3, lty=3) legend(x = 30, y = 80,legend=c("denim", "deSolve"), col = c("#4876ff", "black"), lty=c(1,3)) # ---- Plot D compartment plot(x = mod$Time, y = mod$D, xlab = "Time", ylab = "Count", main="D compartment", col = "#4876ff", type="l", lwd=2) lines(ode_mod$time, ode_mod$D, lwd=3, lty=3) legend(x = 30, y = 60,legend=c("denim", "deSolve"), col = c("#4876ff", "black"), lty=c(1,3)) ```