I am trying to estimate a parameter for a Lorenz system. Although this system is well known to be chaotic, it is well behaved at the beginning of its evolution. For example here are 32 paths for X, Y and Z starting at (1.0, 1.0, 1.0) perturbed by N(0, 0.2).
model Lorenz { const rho = 45.92 const beta = 4.0 const alpha = 16.0
state X, Y, Z obs X_obs
sub initial { X ~ log_normal(log(1.0), 0.00002) Y ~ log_normal(log(1.0), 0.00002) Z ~ log_normal(log(1.0), 0.00002) }
sub transition(delta = 0.0001) { ode { dX/dt = alpha * (Y - X) dY/dt = X * (rho - Z) - Y dZ/dt = X * Y - beta * Z } }
sub observation { X_obs ~ normal(X, 0.2) }}
model LorenzState { const rho = 45.92 const beta = 4
const h = 0.1; // time step const delta_abs = 1.0e-3; // absolute error tolerance const delta_rel = 1.0e-6; // relative error tolerance
state X, Y, Z state ln_alpha
param mu, sigma
noise w
obs X_obs
sub parameter { mu ~ uniform(12.0, 20.0) sigma ~ uniform(0.0, 0.5) }
sub proposal_parameter { mu ~ truncated_gaussian(mu, 0.02, 12.0, 20.0); sigma ~ truncated_gaussian(sigma, 0.01, 0.0, 0.5); }
sub initial { X ~ log_normal(log(1.0), 0.2) Y ~ log_normal(log(1.0), 0.2) Z ~ log_normal(log(1.0), 0.2) ln_alpha ~ gaussian(log(mu), sigma) }
sub transition(delta = h) { w ~ normal (0.0, sqrt(h)) ode(h = h, atoler = delta_abs, rtoler = delta_rel, alg = 'RK4(3)') { dX/dt = exp(ln_alpha) * (Y - X) dY/dt = X * (rho - Z) - Y dZ/dt = X * Y - beta * Z dln_alpha/dt = -sigma * sigma / 2 - sigma * w / h } }
sub observation { X_obs ~ log_normal(X, 0.2) }}
library('rbi')library(ggplot2)
model_file_name <- "LorenzGenerate.bi"
Lorenz <- bi_model(model_file_name)
T <- 2.0nObs <- 100init_parameters <- list(X = 1, Y = 1, Z = 1)
synthetic_dataset <- bi_generate_dataset(end_time=T, model=Lorenz, init=init_parameters, noutputs = nObs)
synthetic_data <- bi_read(synthetic_dataset)
synthetic_df <- as.data.frame(synthetic_data)
ggplot(synthetic_df, aes(X.time)) + geom_path(aes(y = X.value, colour="X.value")) + geom_path(aes(y = X_obs.value, colour="X_obs.value")) + theme(legend.position="bottom") + ggtitle("Lorenz") + theme(plot.title = element_text(hjust = 0.5)) + xlab("Time") + ylab("Value")
model_file_name_state <- "LorenzState.bi"
LorenzState <- bi_model(model_file_name_state)
bi_state <- libbi(model=LorenzState)bi_state
bi_state <- filter(bi_state, nparticles = 4, nthreads = 1, end_time = T, obs = synthetic_dataset, init = init_parameters, ess_rel=1)
> output$logweight$value[1:20] [1] -8.195611 -28.326456 -44.079191 -56.012739 -84.718022 [6] -138.593273 -263.272262 -551.239571 -1135.085810 -2370.284954[11] -4742.979880 -8895.140169 -15225.553036 -23177.856321 -31058.553846[16] -37130.818336 -41081.672274 NaN NaN NaN> output$logweight$value[102:121] [1] -8.195611 -29.026216 -46.347489 -55.193830 -81.421323 [6] -138.593273 -261.459513 -537.840374 -1135.085810 -2370.284954[11] -4742.979880 -8962.658035 -15225.553036 -23177.856321 -31058.553846[16] -37130.818336 -41082.880530 NaN NaN NaN> output$logweight$value[203:222] [1] -8.195611 -29.915752 -43.809861 -55.193830 -81.421323 [6] -138.593273 -263.271111 -537.840374 -1135.085810 -2370.284954[11] -4742.979880 -8896.327921 -15230.638953 -23177.856321 -31058.553846[16] -37130.818336 -41067.499170 NaN NaN NaN> output$logweight$value[304:323] [1] -8.195611 -28.292957 -43.809861 -55.193830 -81.421323 [6] -138.593273 -264.276304 -537.840374 -1135.085810 -2370.284954[11] -4742.979880 -8928.661460 -15225.553036 -23177.856321 -31058.553846[16] -37130.818336 -41006.349718 NaN NaN NaN
Great catch 😀 Thanks that has saved me a lot of work.
Now you can see that the X state value is being tracked quite nicely even beyond where the system becomes chaotic
and more importantly the parameter (which I modelled as Brownian motion) is giving a nice and similar answer for both systems even thought the states have diverged.