I am planning on implementing a kalman filter for a dynamic model and I’m working my way up from an ODE to make sure the basic results can be validated with simpler models and simulations. I went through the simple harmonic oscillator example, and tried to implement a almost as simple SIR disease transmission model. The differential equations are given by
\displaystyle \frac{dS}{dt} = -\frac{\beta}{N} I S
\displaystyle \frac{dI}{dt} = \frac{\beta}{N} I S - \gamma I
\displaystyle \frac{dR}{dt} = \gamma I
Stan code is at the end of the post.
With fixed parameters I get the expected output:
However, when trying to fit the incidence term (\beta / N) I S to simulated data Stan takes a long time and either fails with several warnings like Exception: integrate_ode_rk45: initial state[1] is inf, but must be finite!
, or takes a long time and the estimates are completely wrong. In some cases it will say the poisson rate is negative or zero, but even with quick fixes like iterating over that array and replacing that with a small positive value the other problems persist.
Oddly, when running several chains one or two may actually sample properly. Also if trying to fit I or using normal
for the likelihood may have better results, but I don’t see why it should matter. I also tried changing the ODE integrator, but that seemed to make no difference.
I left the priors flat on purpose for now; this shouldn’t be an issue if everything else is working. Is there something else I may be missing or should check?
I’m using CmdStanPy, and ran a few CmdStan chains as well with similar outputs.
Thank you.
functions {
real[] SIR(real t, // time
real[] v, // state
real[] theta, // parameters
real[] x_r, // data (real)
int[] x_i) { // data (integer)
real dvdt[3];
dvdt[1] = -(theta[1]/theta[3])*v[2]*v[1]; // dS/dt = -(beta/N)*I*S;
dvdt[2] = (theta[1]/theta[3])*v[2]*v[1] - theta[2]*v[2]; // dI/dt = (beta/N)*I*S - gama*I
dvdt[3] = theta[2]*v[2]; // N - S - I; // dR/dt = gama*I
return dvdt;
}
}
data {
int<lower=1> T;
int<lower=0> incidence[T];
real t0;
real ts[T];
}
transformed data {
real x_r[0];
int x_i[0];
}
parameters {
real<lower=0> S0;
real<lower=0> I0;
real<lower=0> Ro;
real<lower=0> gama;
real<lower=0> N;
}
transformed parameters {
real beta = Ro*gama;
real theta[3];
theta[1] = beta;
theta[2] = gama;
theta[3] = N;
real v0[3];
v0[1] = S0;
v0[2] = I0;
v0[3] = N - I0 - S0; // recovered_0
real v_pred[T,3] = integrate_ode_rk45(SIR, v0, t0, ts, theta, x_r, x_i);
vector[T] susc_pred = col( to_matrix(v_pred), 1);
vector[T] inf_pred = col( to_matrix(v_pred), 2);
vector[T] inc_pred = (beta/N)*inf_pred.*susc_pred;
}
model {
incidence[t] ~ poisson(inc_pred);
}