Parameter Estimation of ODE models

SIR model diagram

SIR model definition

SIR <- function(t, state, parameters) {# SIR is the name of this function with inputs t, state, parameters 
  # with function executes lines in {} (differential equations) using values defined in state and parameters
  with(as.list(c(state, parameters)),{  
      dS <- rBirth + rS*R - rI*S*I #ODE for susceptible population
      dI <- rI*S*I - rR*I - rDeath*I #ODE for infected population
      dR <- rR*I - rS*R #ODE for recovered population
      list(c(dS, dI, dR)) #Return change in variables
    })
  }

Cost function definition (Sum of squares)

SSE <- function(parameters){
  names(parameters) <- c("rBirth","rDeath","rI","rR","rS")
  
  options(warn = -1)
  on.exit(options(warn = 0))
  capture.output(
    out <- ode(y = init, times = times, func = SIR, parms = parameters)
  )
  
  fit <- out[out[,"time"]%in%dat[,"time"],-1]
  sse <- sum((dat[,c("S","I","R")] - fit[,c("S","I","R")])^2)
  
  return(sse)
}

SIR data

dat <- data.frame(time=c(0,1,5,10,20,50,100),
                  S=c(990,984,944,820,415,230,273),
                  I=c(10,13,32,86,151,4,0.008),
                  R=c(0,0.56,4.71,18,82,120,74)
                  )

init <- as.numeric(dat[1,-1])
names(init) <- c("S","I","R")
times <- seq(from = 0,to = 100, by = 0.1) #Definition of time (initial,final,step size)

Gradient search (box constrained)

optLBFGS <- optim(bestPar,SSE,method = "L-BFGS-B",
                              lower = rep(0,length(bestPar)),
                              upper = c(5,0.5,0.001,0.5,0.5),
                              control=list(maxit=1000)
                              )

out <- ode(y = init,#Initial conditions 
           times = times, #Time definition
           func = SIR, #Name of function defining ODEs
           parms =  optLBFGS$par #Parameter values
           )

plot(out[,"time"],out[,"S"],col=3,lwd=2,type="l",xlab="Time",ylab="Population size",ylim=c(0,1000)) #Plot S population vs time
lines(out[,"time"],out[,"I"],col=2,lwd=2,type="l") #Plot I population vs time
lines(out[,"time"],out[,"R"],col=4,lwd=2,type="l") #Plot R population vs time
points(dat[,"time"],dat[,"S"],pch=19,col=3)
points(dat[,"time"],dat[,"I"],pch=19,col=2)
points(dat[,"time"],dat[,"R"],pch=19,col=4)
legend("topright",legend = c("S","I","R"),col = c(3,2,4),lwd = 2) #Define plot legend
text(20,950,sprintf("SSE: %.1f",optLBFGS$value),cex=1,pos=4)

Genetic algorithm

GA <- ga(type = "real-valued",
         fitness = function(par) -SSE(par),
         lower = rep(0,length(bestPar)), 
         upper = c(0.2,0.2,0.001,0.1,0.1),
         names = c("rBirth","rDeath","rI","rR","rS"),
         popSize = 25, 
         maxiter = 1000, 
         run = 200,
         optim = TRUE,
         optimArgs = list(pressel = 0.8,control = list(maxit = c(10,100)))
         )

bestGA <- as.vector(GA@solution)
names(bestGA) <- c("rBirth","rDeath","rI","rR","rS")

out <- ode(y = init,#Initial conditions 
           times = times, #Time definition
           func = SIR, #Name of function defining ODEs
           parms =  bestGA #Parameter values
           )

plot(out[,"time"],out[,"S"],col=3,lwd=2,type="l",xlab="Time",ylab="Population size",ylim=c(0,1000)) #Plot S population vs time
lines(out[,"time"],out[,"I"],col=2,lwd=2,type="l") #Plot I population vs time
lines(out[,"time"],out[,"R"],col=4,lwd=2,type="l") #Plot R population vs time
points(dat[,"time"],dat[,"S"],pch=19,col=3)
points(dat[,"time"],dat[,"I"],pch=19,col=2)
points(dat[,"time"],dat[,"R"],pch=19,col=4)
legend("topright",legend = c("S","I","R"),col = c(3,2,4),lwd = 2) #Define plot legend
text(20,950,sprintf("SSE: %.1f",abs(GA@fitnessValue)),cex=1,pos=4)