Skip to content

Commit 2098a5d

Browse files
committed
Address BB comments and remove rstan dependency.
1 parent b187bc1 commit 2098a5d

File tree

11 files changed

+114
-845
lines changed

11 files changed

+114
-845
lines changed

knitr/planetary_motion/init/init1.r

Lines changed: 0 additions & 7 deletions
This file was deleted.

knitr/planetary_motion/model/planetary_motion.stan

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ transformed data {
4949

5050
parameters {
5151
real<lower = 0> k;
52-
// real<lower = 0> sigma_x;
53-
// real<lower = 0> sigma_y;
5452
}
5553

5654
transformed parameters {
@@ -60,17 +58,13 @@ transformed parameters {
6058
}
6159

6260
model {
63-
// sigma_x ~ normal(0, 1);
64-
// sigma_y ~ normal(0, 1);
6561
k ~ normal(0, 1);
66-
// k ~ normal(1, 0.1);
6762

6863
q_obs[, 1] ~ normal(y[, 1], sigma_x);
6964
q_obs[, 2] ~ normal(y[, 2], sigma_y);
7065
}
7166

7267
generated quantities {
73-
// real q_pred[n, 2];
7468
real qx_pred[n];
7569
real qy_pred[n];
7670

knitr/planetary_motion/model/planetary_motion_star2.stan renamed to knitr/planetary_motion/model/planetary_motion_star.stan

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,7 @@ data {
3333
transformed data {
3434
real t0 = 0;
3535
int n_coord = 2;
36-
// real q0[n_coord] = {1.0, 0.0};
37-
// real p0[n_coord] = {0.0, 1.0};
38-
// real y0[n_coord * 2] = append_array(q0, p0);
39-
4036
real m = 1.0;
41-
42-
// real t[n];
43-
// for (i in 1:n) t[i] = i * 1.0 / 10;
44-
4537
int x_i[0];
4638

4739
real<lower = 0> sigma_x = sigma;
@@ -78,14 +70,12 @@ model {
7870
p0[2] ~ lognormal(0, 1); // impose p0 to be positive.
7971
q0 ~ normal(0, 1);
8072
star ~ normal(0, 0.5);
81-
// star ~ normal(0, 1);
8273

8374
q_obs[, 1] ~ normal(y[, 1], sigma_x);
8475
q_obs[, 2] ~ normal(y[, 2], sigma_y);
8576
}
8677

8778
generated quantities {
88-
// real q_pred[n, 2];
8979
real qx_pred[n];
9080
real qy_pred[n];
9181

-111 KB
Binary file not shown.

knitr/planetary_motion/planetary_motion.rmd

Lines changed: 101 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,32 @@ In our presentation, we try to distinguish generalizable methods, problem-specif
4141

4242
## R setup {-#setup}
4343

44-
```{r message = FALSE}
44+
```{r include=FALSE}
4545
# Adjust to your setting
4646
.libPaths("~/Rlib/")
47+
setwd("~/Code/example-models/knitr/planetary_motion")
48+
```
4749

50+
```{r message = FALSE}
51+
library(dplyr)
4852
library(cmdstanr)
49-
set_cmdstan_path("~/Rlib/cmdstan/")
50-
library(rstan)
53+
library(posterior)
5154
library(ggplot2)
52-
5355
library(plyr)
5456
library(tidyr)
55-
library(dplyr)
57+
5658
library(boot)
5759
library(latex2exp)
5860
source("tools.r")
5961
6062
set.seed(1954)
6163
```
6264

65+
```{r include=FALSE}
66+
# Adjust to your setting
67+
set_cmdstan_path("~/Rlib/cmdstan/")
68+
```
69+
6370
All the requisite code to run this notebook can be found online, in the [planetary motion github repository](https://github.com/stan-dev/example-models/tree/case-study/planet/knitr/planetary_motion).
6471

6572
# Building the model
@@ -102,23 +109,20 @@ and we set $k = 1$.
102109
mod <- cmdstan_model("model/planetary_motion_sim.stan")
103110
104111
n <- 40
105-
sim <- mod$sample(data = list(n = n, sigma_x = 0.01, sigma_y = 0.01),
106-
chains = 1, iter_warmup = 1,
112+
sigma = 0.01
113+
sim <- mod$sample(data = list(n = n, sigma_x = sigma, sigma_y = sigma),
114+
chains = 1, iter_warmup = 1,
107115
iter_sampling = 2, seed = 123)
108116
109-
r_sim <- read_stan_csv(sim$output_files())
110-
simulation <- rstan::extract(r_sim, pars = "q_obs")$q_obs[1, , ]
111-
112-
q_x <- simulation[, 1]
113-
q_y <- simulation[, 2]
117+
simulation <- as.vector(sim$draws(variables = "q_obs")[1, , ])
114118
115119
q_obs <- array(NA, c(n, 2))
116-
q_obs[, 1] <- q_x
117-
q_obs[, 2] <- q_y
120+
q_obs[, 1] <- simulation[1:40]
121+
q_obs[, 2] <- simulation[41:80]
118122
119123
sub_set <- seq(from = 1, to = 40, by = 2)
120-
plot <- ggplot(data = data.frame(q_x = q_x[sub_set],
121-
q_y = q_y[sub_set],
124+
plot <- ggplot(data = data.frame(q_x = q_obs[sub_set, 1],
125+
q_y = q_obs[sub_set, 2],
122126
time = sub_set),
123127
aes(x = q_x, y = q_y, label = time)) + theme_bw() +
124128
geom_text()
@@ -154,30 +158,35 @@ chains <- 8
154158
```
155159

156160
```
161+
mod <- cmdstan_model("model/planetary_motion.stan")
157162
fit <- mod$sample(data = list(n = n, q_obs = q_obs),
158163
chains = chains, parallel_chains = chains,
159164
iter_warmup = 500,
160165
iter_sampling = 500,
161166
seed = 123, save_warmup = TRUE)
167+
168+
169+
fit$save_object(file = "saved_fit/fit1.RDS")
162170
```
163171

164172
```{r }
165173
# The model takes a while to run, so we read in the saved output.
166174
r_fit1 <- readRDS("saved_fit/fit1.RDS")
167-
get_elapsed_time(r_fit1)
175+
print(r_fit1$time(), digits = 3)
168176
```
169177

170178
The first notable pathology is that some of the chains take a much longer time to run. The difference is not subtle...
171179

172180
Let's examine the summary.
173181
```{r }
174-
summary(r_fit1, pars = c("lp__", "k"), probs = c())[1]
182+
r_fit1$summary(c("lp__", "k"))[, c(1, 2, 4, 8, 9)]
175183
```
176184
$\hat R \gg 1$.
177185
Wow, these numbers are dramatic!
178186
Clearly the chains are not mixing and we can visualize this using trace plots.
179187
```{r fig.height=3}
180-
traceplot(r_fit1, pars = c("lp__", "k"))
188+
bayesplot::mcmc_trace(r_fit1$draws(),
189+
pars = c("lp__", "k"))
181190
```
182191

183192

@@ -200,7 +209,7 @@ We can check for degeneracy by looking at the _posterior predictive checks_, sp
200209
We plot $q_x$ against $q_y$, and for each chain, compute the median estimate for $q_\mathrm{pred}$, obtained using the `generated quantities block`.
201210
Note that since we fixed $\sigma = 0.01$, we expect the confidence interval to be very narrow.
202211

203-
```{r message=FALSE}
212+
```{r }
204213
data_pred <- data.frame(q_obs, 1:n)
205214
names(data_pred) <- c("qx", "qy", "t")
206215
@@ -223,8 +232,8 @@ Based on the trace plots, the chains appear to be relatively static during the s
223232
We extend the trace plots to include the warmup phase.
224233

225234
```{r fig.height=3}
226-
traceplot(r_fit1, pars = c("lp__", "k"),
227-
inc_warmup = TRUE)
235+
# TODO: added shaded area for warm-up phase.
236+
bayesplot::mcmc_trace(r_fit1$draws(inc_warmup = TRUE), pars = c("lp__", "k"))
228237
```
229238

230239
It is now clear that the chain's final position is mostly driven by its initial point.
@@ -316,12 +325,15 @@ plot <- ggplot() + geom_path(data = plot_data[select, ],
316325
geom_point(aes(x = q_obs[comp_point, 1], y = q_obs[comp_point, 2])) +
317326
# Add segments to compare distances.
318327
geom_segment(aes(x = q_obs[comp_point, 1], y = q_obs[comp_point, 2],
319-
xend = q_216[comp_point], yend = q_216[comp_point, 2]),
328+
xend = q_216[comp_point],
329+
yend = q_216[comp_point, 2]),
320330
linetype = "dashed") +
321331
geom_segment(aes(x = q_obs[comp_point, 1], y = q_obs[comp_point, 2],
322-
xend = q_160[comp_point, 1], yend = q_160[comp_point, 2]),
332+
xend = q_160[comp_point, 1],
333+
yend = q_160[comp_point, 2]),
323334
linetype = "dashed") +
324-
theme(text = element_text(size = 18)) + xlab(TeX("$q_x$")) + ylab(TeX("$q_y$"))
335+
theme(text = element_text(size = 18)) +
336+
xlab(TeX("$q_x$")) + ylab(TeX("$q_y$"))
325337
326338
plot
327339
```
@@ -426,10 +438,9 @@ fit <- mod$sample(data = list(n = n, q_obs = q_obs),
426438
iter_sampling = 500,
427439
seed = 123, save_warmup = TRUE, refresh = 0)
428440
429-
r_fit2 <- read_stan_csv(fit$output_files())
430-
summary(r_fit2, pars = c("lp__", "k"), probs = c())[1]
441+
fit$summary(c("lp__", "k"))[, c(1, 2, 4, 8, 9)]
431442
432-
traceplot(r_fit2, pars = c("lp__", "k"))
443+
bayesplot::mcmc_trace(fit$draws(c("lp__", "k")))
433444
```
434445

435446
Everything now looks good. The chains converge near $k = 1$, and simulate predictions that are consistent with the data.
@@ -514,7 +525,13 @@ Relaxing the above a little, we may roll with a prior such as $k \sim \mathrm{No
514525

515526
Let us fit the model, using the initial conditions we developed above, plus a broad range of values for the star's position, confined by the observations.
516527
```{r }
517-
model_name <- "planetary_motion_star2.stan"
528+
mod <- cmdstan_model("model/planetary_motion_star.stan")
529+
530+
# Process data for new model (same data, different format)
531+
n_select <- 40
532+
time <- (1:n_select) / 10
533+
stan_data <- list(n = n_select, q_obs = q_obs, time = time,
534+
sigma = sigma)
518535
519536
init_empirical <- function() {
520537
p0_empirical <- (q_obs[2, ] - q_obs[1, ]) /
@@ -529,45 +546,49 @@ init_empirical <- function() {
529546
)
530547
}
531548
532-
# Process data for new model (same data, different format)
533-
n_select <- 40
534-
time <- (1:n_select) / 10
535-
stan_data <- list(n = n_select, q_obs = q_obs, sigma = sigma,
536-
time = time)
537-
538549
chains <- 8
539-
for (i in 1:chains) {
540-
init_chain <- init_empirical()
541-
with(init_chain, stan_rdump(ls(init_chain),
542-
file = paste0("init/init", i, ".r")))
550+
init_files <- paste0("init/planetary_motion_star/init",
551+
1:chains, ".json")
552+
if (FALSE) { # Set to TRUE to generate new inits;
553+
# else use existing inits.
554+
for (i in 1:chains) {
555+
init_chain <- init_empirical()
556+
write_stan_json(init_chain, file = init_files[i])
557+
}
543558
}
544-
545-
init_files <- paste0("init/init", 1:chains, ".r")
546-
559+
```
560+
```
547561
# (Code takes time to run; instead use saved output)
548-
# fit <- mod$sample(data = stan_data,
549-
# init = init_files,
550-
# chains = chains, parallel_chains = chains,
551-
# iter_warmup = 500,
552-
# iter_sampling = 500,
553-
# seed = 123, save_warmup = TRUE)
554-
r_fit <- readRDS(file = "saved_fit/planetary_motion_star2_pathology.stan.RDS")
555-
556-
get_elapsed_time(r_fit)
562+
fit <- mod$sample(data = stan_data,
563+
init = init_files,
564+
chains = chains, parallel_chains = chains,
565+
iter_warmup = 500,
566+
iter_sampling = 500,
567+
seed = 123, save_warmup = TRUE)
568+
fit$save_object(file = "saved_fit/fit_star.RDS")
557569
```
558-
As commented before, we would've been wise not to run the algorithm for so many iterations...
559570

571+
```{r }
572+
r_fit2 <- readRDS(file = "saved_fit/fit_star.RDS")
573+
print(r_fit2$time(), digits = 3)
574+
```
575+
As commented before, we would've been wise not to run the algorithm for so many iterations... Stan returns several warning messages, including divergent transitions (for 343 out of 8,000 samples) and exceeded maximum treedepths (for 28 samples).
576+
We can check the warning message report using `fit$cmdstan_diagnose()`.
577+
As before, let's examine a few diagnostics:
560578
```{r, message=FALSE}
561579
pars <- c("lp__", "k", "q0", "p0", "star")
562-
summary(r_fit, pars = pars, probs = c())[1]
563-
traceplot(r_fit, pars = pars, inc_warmup = TRUE)
580+
r_fit2$summary(pars)[, c(1, 2, 4, 8, 9)]
581+
582+
pars <- c("lp__", "k", "q0[1]", "q0[2]", "p0[1]", "p0[2]",
583+
"star[1]", "star[2]")
584+
bayesplot::mcmc_trace(r_fit2$draws(), pars = pars)
564585
```
565586

566587
We have five well-behaved chains, which return consistent estimates, and three other chains which have with great effort ventured into other regions of the parameter space.
567588
They take significantly longer to run without achieving the same log-posterior (what else is new?).
568589
The posterior predictive checks confirm that these three chains don not produce output consistent with the observations.
569590
```{r message=FALSE, warning=FALSE}
570-
ppc_plot2D(r_fit, data_pred = data_pred, plot_star = TRUE)
591+
ppc_plot2D(r_fit2, data_pred = data_pred, plot_star = TRUE)
571592
```
572593
The particular characteristic they share in common is that $q_0$ and $q_*$ are very close to one another.
573594

@@ -673,7 +694,7 @@ init_empirical <- function() {
673694
p0_empirical <- (q_obs[2, ] - q_obs[1, ]) /
674695
(stan_data$time[2] - stan_data$time[1])
675696
q0_empirical <- q_obs[1, ]
676-
sigma <- 0.1
697+
sigma <- 0.5
677698
678699
list(k = abs(rnorm(1, 0, 1)),
679700
p0 = c(rnorm(2, p0_empirical, sigma)),
@@ -685,26 +706,36 @@ init_empirical <- function() {
685706
chains <- 8
686707
687708
# create init files for each chain
688-
for (i in 1:chains) {
689-
init_chain <- init_empirical() # init_empirical2() # init()
690-
with(init_chain, stan_rdump(ls(init_chain),
691-
file = paste0("init/init", i, ".r")))
709+
init_files <- paste0("init/planetary_motion_star2/init",
710+
1:chains, ".json")
711+
if (FALSE) { # Set to TRUE to generate new inits;
712+
# elese use existing inits.
713+
for (i in 1:chains) {
714+
init_chain <- init_empirical()
715+
write_stan_json(init_chain, file = init_files[i])
716+
}
692717
}
718+
```
719+
```
720+
# (Code takes time to run; instead use saved output)
721+
fit <- mod$sample(data = stan_data,
722+
init = init_files,
723+
chains = chains, parallel_chains = chains,
724+
iter_warmup = 500,
725+
iter_sampling = 500,
726+
seed = 123, save_warmup = TRUE)
727+
fit$save_object(file = "saved_fit/fit_star2.RDS")
728+
```
693729

694-
# fit <- mod$sample(data = stan_data,
695-
# init = init_files,
696-
# chains = chains, parallel_chains = chains,
697-
# iter_warmup = 500,
698-
# iter_sampling = 500,
699-
# seed = 123, save_warmup = TRUE)
700-
r_fit <- readRDS(file = "saved_fit/planetary_motion_star2.stan.RDS")
730+
```{r }
731+
r_fit3 <- readRDS(file = "saved_fit/fit_star2.RDS")
701732
702-
summary(r_fit, pars = pars, probs = c())[1]
733+
r_fit3$summary(pars)[, c(1, 2, 4, 8, 9)]
703734
704-
ppc_plot2D(r_fit, data_pred = data_pred, plot_star = TRUE)
735+
ppc_plot2D(r_fit3, data_pred = data_pred, plot_star = TRUE)
705736
```
706737

707-
All our diagnostics now suggest we have sucessfully fitted the model.
738+
All our diagnostics now suggest we have successfully fitted the model.
708739

709740
# Discussion and lessons learned
710741

-842 KB
Binary file not shown.

0 commit comments

Comments
 (0)