Skip to contents

Comparison between deSolve and denim

## Warning: package 'deSolve' was built under R version 4.3.1
# --- Timestep definition
simulationDuration <- 300 
timestep <- 0.01 # small timestep required for comparison

# --- Transition def for denim
transitions <- list(
  "S -> I" = "beta * S * (I/N) * timestepDur",
  "I -> R" = d_gamma(3, 2)
)
parameters <- c(beta = 0.2, scale = 3, shape=2, N=1000, timestepDur=timestep) 
initialValues <- c(S = 999, I = 1, I1 = 1, I2=0, R=0)

# --- Transition def for deSolve
transition_func <- function(t, state, param){
  with(as.list( c(state, param) ), {
      gamma_rate = 1/scale
      
      dS = - beta * S * I/N
      # apply linear chain trick
      dI1 = beta * S * I/N - gamma_rate*I1
      dI2 = gamma_rate*I1 - gamma_rate*I2
      dI =  dI1 + dI2
      dR = gamma_rate*I2
      list(c(dS, dI, dI1, dI2, dR))
  })
}

Run simulation with denim

denim_runtime <- bench::mark(
  sim(transitions = transitions, initialValues = initialValues, parameters, simulationDuration = simulationDuration, timeStep = timestep),
  iterations = 1
)
denim_runtime <- as.numeric(denim_runtime$total_time)

mod <- sim(transitions = transitions, initialValues = initialValues, parameters, simulationDuration = simulationDuration, timeStep = timestep)
# --- show output
head(mod[mod$Time %in% 1:simulationDuration,])
##     Time        S        I          R
## 101    1 998.7825 1.169597 0.04786644
## 201    2 998.5358 1.296591 0.16756473
## 301    3 998.2665 1.399083 0.33440701
## 401    4 997.9783 1.487980 0.53374794
## 501    5 997.6732 1.569868 0.75697598
## 601    6 997.3521 1.648745 0.99910771

Run simulation with deSolve

times <- seq(0, simulationDuration, timestep)

desolve_runtime <- bench::mark(
  ode(y = initialValues, times = times, parms = parameters, func = transition_func),
  iterations = 1
)
## Warning: Some expressions had a GC in every iteration; so filtering is
## disabled.
desolve_runtime <- as.numeric(desolve_runtime$total_time)

ode_mod <- ode(y = initialValues, times = times, parms = parameters, func = transition_func)

# --- show output
ode_mod <- as.data.frame(ode_mod)
head(ode_mod[ode_mod$time %in% 1:simulationDuration, c("time", "S", "I", "R")])
##     time        S        I         R
## 101    1 998.7824 1.169731 0.0479167
## 201    2 998.5355 1.296752 0.1677463
## 301    3 998.2660 1.399189 0.3347737
## 401    4 997.9777 1.487967 0.5343309
## 501    5 997.6725 1.569688 0.7577887
## 601    6 997.3515 1.648357 1.0001499

Execution time comparison

denim takes approximately 4.3 times as long as deSolve to compute the result with the given specifications .

This significant difference can be attributed to the difference in approaches: deSolve solves a system of ODEs while denim iterates through each timestep and updates the population in each compartment

While the approach in denim allow more flexibility in types of dwell time distributions, the computation time scales up as timestep grows smaller (in O(n) time complexity).

Plot the result

# increase timestep before plotting
mod <- mod[mod$Time %in% seq(0, simulationDuration, 0.2),]
ode_mod <- ode_mod[ode_mod$time %in% seq(0, simulationDuration, 0.2),]

Comparison with SEIR model

Implementation using deSolve

model definition deSolve
library(deSolve)
parameters <- c(scale_I = 4, shape_I=2,
                scale_R = 3, shape_R = 2,
                timeStepDur = 1, R0 = 3.5, N = 1e6) 
initialValues <- c(S = 999999, E1 = 1,
                   E2 = 0, E = 0, I1=0, 
                   I2=0, I=0, R=0
                   )

# --- Transition def for deSolve
transition_func <- function(t, state, param){
  with(as.list( c(state, param) ), {
      gamma_rate_I = 1/scale_I
      gamma_rate_R = 1/scale_R
      
      tr = scale_R*shape_R
      
      dS = - (R0/tr) * S * I/N
      # apply linear chain trick
      dE1 = (R0/tr) * S * I/N - gamma_rate_I*E1
      dE2 = gamma_rate_I*E1 - gamma_rate_I*E2
      dE = dE1 + dE2
      dI1 = gamma_rate_I*E2 - gamma_rate_R*I1
      dI2 = gamma_rate_R*I1 - gamma_rate_R*I2
      dI =  dI1 + dI2

      dR = gamma_rate_R*I2
      list(c(dS, dE1, dE2, dE, dI1, dI2, dI, dR))
  })
}

times <- seq(0, 210, 1)

ode_mod <- ode(y = initialValues, times = times, parms = parameters, func = transition_func)

# --- show output
ode_mod <- as.data.frame(ode_mod)

Implementation using denim

model definition in denim
denim_model <- list(
  "S -> E" = "(R0/tr) * timeStepDur * S * (I/N)", # formulate according that of uSEIR method
  "E -> I" = d_gamma(scale = 4, shape = 2),
  "I -> R" = d_gamma(scale = 3, shape = 2)
)

initialValues <- c(S = 999999, E = 1, I= 0, R= 0)
parameters <- c(R0 = 3.5, 
                tr = 3*2, # compute mean recovery time, for gamma it's scale*shape
                N = 1e6, timeStepDur = 0.01)

mod <- sim(transitions = denim_model, 
                     initialValues = initialValues,
                     parameters = parameters,
                     simulationDuration = 210, timeStep = 0.01)

# denim_out[, c("S","E", "I", "R")] <- denim_out[, c("S","E", "I", "R")]/1e6
plot(mod)

Plot result