Here we employ our previous equations to simulate changes in the infection frequency over space and time.
One-dimensional habitat
spatial_sis_1d <- function(num_gens, L, pop_size, beta, gamma, m,d){
f0 = 1/pop_size # set initial fraction of infected
mid<-floor(L/2) # set initial location
I <- array(data=0,dim = c(num_gens,L+1)) # initialize matrix of I values
for(i in 1:(num_gens-1)){ # loop of all time
if(sum(I[i,] > 0)==0){ # check if infection is extinct, if so reset
I[i,mid] <- f0
}
for(j in 2:L){ # loop for all space
# difference equation
dI = beta*I[i,j]*(1-I[i,j]) - gamma*I[i,j] + (m-d)*(I[i,j-1] + I[i,j+1] - 2*I[i,j])
p = I[i,j] + dI # update frequency
p[which(p>1)] = 1 # bound to be less than 1
p[which(p<0)] = 0 # bound to be greater than 0
I[i+1,j] = rbinom(n = length(p),size = pop_size, prob = p)/pop_size # sample using binomial
}
}
return(I)
}
num_gens = 1e3 # number of time generations to simulate
L = 50 # number of demes
pop_size = 1e4 # population size N
beta = 0.2875 # transmission rate
gamma = 1/8 # recovery rate
m = 1e-1 # migration rate
d=0 # distancing
I_1d <- spatial_sis_1d(num_gens,L,pop_size,beta,gamma,m,d)
Time series in one deme
We plot changed in infection frequency at one location over time.
mid <- floor(L/2) # identify starting location of epidemic
plot(I_1d[,mid+10],type="l", main ="I vs. time in 1 deme",
xlab="time (generations)",
ylab ="fraction infected")
Heatmap over time
We show infection spread over time on x-axis and \(I\) on y-axis.
time <- 1:num_gens
position <- 1:(L+1)
time_interval <-0:300 # choose a time interval to plot from infection freqs
image(time[time_interval],position,I_1d[time_interval,])
We see a gradual spread of the infection until an equilibrium where all demes have a large fraction of \(I\).
We’ve successfully replicated the one-dimensional Stepping Stone model and captured typical stochastic SIS dynamics.
Two-dimensions with laplacian
We expand the one-dimensional Stepping Stone to two spatial dimensions. We now allow for migration along a vertical axis. The following schematic illustrates this:
A schematic of the two-dimensional Stepping Stone model by Kimura.
Each deme has a coordinate \((r,s)\). We can describe the changes in frequency from migration in the two-dimensional model as follows:
\[
I_{r,s}(t+1) - I_{r,s}(t)= m(f_{r-1,s} + f_{r+1,s} + f_{r,s+1} + f_{r,s-1} - 4f_{r,s})
\]
However, to avoid having to deal with all of the subscripts and loops, we compute the Laplacian of \(I\). The discrete Laplacian of a function \(\psi(x,y)\) (written \(\nabla^2 \psi(x,y)\)) is defined as the following:
\[
\nabla^2 \psi(x,y) = \frac{1}{h^2}(\psi_{x+h,y} + \psi_{x-h,y} + \psi_{x,y+h} + \psi_{x,y-h} - 4\psi_{x,y})
\]
Exactly the same as the form of the two-dimensional Stepping Stone! We use the R package spatialFil
to directly compute the Laplacian on our matrix. When we call the applyFilter() function on \(I\), the calculation in our model equation is performed across all pairs of \((r,s)\) neighbor demes and \(dI\) is computed accordingly. All this is done in a computationally efficient, generalized fashion.
library("spatialfil")
# With laplacian
spatial_sis_2d <- function(num_gens,L,pop_size,beta,gamma,m,d){
f0 = 1/pop_size # initial freq
mid<-floor(L/2) # initial location
I <- array(data=0,dim = c(num_gens,L,L)) # intialize infection matrix
for(i in 1:(num_gens-1)){ # loop for time
if(sum(I[i,,] > 0)==0){ # check if extinct, if so reset
I[i,mid,mid] <- f0
}
# compute discrete laplace
laplace_I = applyFilter(x = I[i,,], kernel = convKernel(sigma = 1, k = 'laplacian'))
# compute diff eq
dI = beta*I[i,,]*(1-I[i,,]) - gamma*I[i,,] + (m-d)*laplace_I
p = I[i,,] + dI # update freqs
p[which(p>1)] = 1 # bound freqs
p[which(p<0)] = 0
# generate random draws for entire 2d spatial array
rand = rbinom(n = length(p),size = pop_size, prob = p)/pop_size
dim(rand) = c(L,L) # reshape 2d rng vector back into lattice shape
I[i+1,,] = rand # update infections
}
return(I)
}
num_gens = 1e4
L = 50
pop_size = 1e4
beta = 0.2875
gamma = 1/8
m = 1e-1
d = 0
I_2d <- spatial_sis_2d(num_gens,L,pop_size,beta,gamma,m,d)
Plotting time series
mid <- floor(L/2)
plot(I_2d[0:1000,mid,mid],type="l", main ="I vs. time in 1 deme",
xlab="time (generations)",
ylab ="fraction infected")
Heatmap of infection frequencies
position <- 1:L
time <- 130
image(position,position,I_2d[time,,])
Making a .gif of infection frequencies
# dir.create("gif") # make a new directory for images
# setwd("gif/") # set as wd
#
# position = 1:L # all positions
#
# for(i in 1:num_gens){ # loops for time
# png(file=paste("gif_",i,".png")) # take images
#
# image(position,position,I_2d[i,,]) # save images
# dev.off() # end
#
# }
A .gif exemplifying the spatio-temporal dynamics of our model.
Here we see a problem with the discrete Laplacian function used in R. The spatial boundaries of our model are reflecting, i.e. once the infection spreads to the “walls” of the lattice, they reverberate back towards the center and effectively nullify spatial patterns. We will explore this further in the Results section. Still, this .gif demonstrates that the infection will not spread beyond \(L\), the size of the lattice. This answers our question (2)!
We’ve expanded the model to two-spatial dimensions and incorporated SIS dynamics!