Hi Stanimals and ODE experts,
I have a toy ODE problem that has two equations with time varying constants. I’ve coded up the model where the constants for the first 3 time points change and the rest are 0. This is just an example to show that I can kind of hack it to work, however, I’m going to have much longer time spans and lots of changing constants. You can think of the constants measuring a known exogenous input at the given time that disturbs the system as written. Is there a way to get this to work in the ideal case I present below?
functions{
real[] decay(real t, real [] y, real [] theta,
real [] x_r, int[] x_i){
real dydt[2];
real theta2;
if(t <= 1){
theta2 = theta[2] + theta[3] * x_r[1];
dydt[1] = y[2] * x_r[1] - theta[1] * y[1];
dydt[2] = -theta2 * y[2] + (1 - x_i[1]) * theta[1] * (1 - y[2]) ;
} else if (t <= 2 && t > 1){
theta2 = theta[2] + theta[3] * x_r[2];
dydt[1] = y[2] * x_r[2] - theta[1] * y[1];
dydt[2] = -theta2 * y[2] + (1 - x_i[2]) * theta[1] * (1 - y[2]) ;
} else if (t <= 3 && t > 2){
theta2 = theta[2] + theta[3] * x_r[3];
dydt[1] = y[2] * x_r[3] - theta[1] * y[1];
dydt[2] = -theta2 * y[2] + (1 - x_i[3]) * theta[1] * (1 - y[2]) ;
} else {
theta2 = theta[2];
dydt[1] = - theta[1] * y[1];
dydt[2] = -theta2 * y[2] + theta[1] * (1 - y[2]) ;
}
return dydt;
}
}
data {
real theta[3];
int N_t;
real times[N_t];
real C0[2];
real r[N_t];
int x[N_t];
}
parameters{
}
transformed parameters{
real C[N_t, 2];
C = integrate_ode_rk45(decay, C0, 0, times, theta, r, x);
}
model{
}
generated quantities{
real stock[N_t];
real quality[N_t];
stock = C[, 1];
quality = C[, 2];
}
Ideally, I would just be able to call the time-varying constants using t
or some iterator that is indexed off of the number of time points. Something like (I know this doesn’t work since t
is a real
and indexes must be int
):
functions{
real[] decay(real t, real [] y, real [] theta,
real [] x_r, int[] x_i){
real dydt[2];
real theta2;
theta2 = theta[2] + theta[3] * x_r[t];
dydt[1] = y[2] * x_r[t] - theta[1] * y[1];
dydt[2] = -theta2 * y[2] + (1 - x_i[t]) * theta[1] * (1 - y[2]) ;
}
return dydt;
}
}
To run the first ODE sample with dummy data you can use:
library(rstan)
library(data.table)
r <- c(10, 40, 35, rep(0, 12))
input_data <- list(
theta = c(stock_decay = 0.05, quality_wearout = 0.03, repetition_wearout = 0.01),
N_t = length(r),
times = seq(from = 1, to = length(r)),
C0 = c(0, 1),
r = log(1 + r),
x = (r != 0)*1
)
m_test <- stan_model(file = 'de_test.stan')
samples <- sampling(m_test, data=input_data,
algorithm = "Fixed_param",
iter = 1, chain = 1)
X <- extract(samples)
out <- data.frame(
time = input_data$times,
stock = c(X[["stock"]]),
quality = c(X[["quality"]])
)
data <- as.data.table(out)
With output:
time stock quality
1: 1 2.276421 0.9474520
2: 2 5.483019 0.8859394
3: 3 8.211380 0.8294966
4: 4 7.810937 0.8137738
5: 5 7.429992 0.7992599
6: 6 7.067626 0.7858618
7: 7 6.722934 0.7734944
8: 8 6.395054 0.7620780
9: 9 6.083164 0.7515390
10: 10 5.786483 0.7418099
11: 11 5.504273 0.7328291
12: 12 5.235827 0.7245390
13: 13 4.980474 0.7168863
14: 14 4.737573 0.7098217
15: 15 4.506519 0.7033003
Thanks!