Chapter 8 Bonus chapter: Additional models developed by students
8.1 Additional models
Additional models that have been developed by previous students:
8.1.3 Memory agent with exponential memory decay (rate)
Memory = alpha * memory + (1 - alpha) * otherChoice
8.1.4 Memory agent with exponential memory decay (exponential)
The agent uses a weighted memory store, where previous choices are weighted by a factor based on how far back in the past they were made, represented by power. The agent first creates a vector of weights for each of the previous choices based on their position in the memory store and the power value. This model implements exponential decay of memory, according to a power law. Specifically, the opposing agent’s choices up to the present trial are encoded as 1’s and -1’s. The choices are weighted, and then summed. So, on trial i, the memory of the previous trial j is given a weight corresponding to j^(-1). Higher values of j correspond to earlier trials, with j = 1 being the most recent. Then a choice is made based on this weight according to the following formula:
Theta = inv_logit(alpha + beta * weightedMemory)
[N.B. really poor parameter recovery]
8.1.5 Memory agent with exponential memory decay, but separate memory for wins and losses
[N.B. really poor parameter recovery]
8.4 Generate data
[MISSING: EXPLAIN THE MODEL STEP BY STEP]
[MISSING: PARALLELIZE AND MAKE IT MORE SIMILAR TO PREVIOUS DATASETS]
8.5 Sanity check for the data
d <- d %>% group_by(agent, strategy) %>% mutate(
nextChoice = lead(choice),
prevWin = lag(win),
prevLose = lag(lose),
cumulativerate = cumsum(choice) / seq_along(choice),
performance = cumsum(feedback) / seq_along(feedback)
) %>% subset(complete.cases(d)) %>% subset(trial > 1)
p1 <- ggplot(d, aes(trial, cumulativerate, group = agent, color = agent)) +
geom_line() +
geom_hline(yintercept = 0.5, linetype = "dashed") + ylim(0,1) + theme_classic() + facet_wrap(.~strategy)
p1
p2a <- ggplot(subset(d, strategy == "Random"), aes(trial, 1 - performance, group = agent, color = agent)) +
geom_line() +
geom_hline(yintercept = 0.5, linetype = "dashed") + ylim(0,1) + theme_classic()
p2b <- ggplot(subset(d, strategy == "WSLS"), aes(trial, performance, group = agent, color = agent)) +
geom_line() +
geom_hline(yintercept = 0.5, linetype = "dashed") + ylim(0,1) + theme_classic()
library(patchwork)
p2a + p2b
8.6 More sanity check
## Checking lose/win do determine choice
d %>% subset(strategy == "WSLS") %>%
mutate(nextChoice = lead(choice)) %>%
group_by(agent, win, lose) %>%
summarize(heads = mean(nextChoice)) %>%
ggplot(aes(win, heads)) +
geom_point() +
theme_bw() +
facet_wrap(~lose)
## `summarise()` has grouped output by 'agent', 'win'. You can override using the
## `.groups` argument.
## Warning: Removed 100 rows containing missing values (`geom_point()`).
8.8 Create the model
[MISSING: MORE MEANINGFUL PREDICTIONS, BASED ON THE 4 SCENARIOS]
stan_wsls_model <- "
functions{
real normal_lb_rng(real mu, real sigma, real lb) {
real p = normal_cdf(lb | mu, sigma); // cdf for bounds
real u = uniform_rng(p, 1);
return (sigma * inv_Phi(u)) + mu; // inverse cdf for value
}
}
data {
int<lower = 1> trials;
array[trials] int h;
vector[trials] win;
vector[trials] lose;
}
parameters {
real alpha;
real winB;
real loseB;
}
model {
target += normal_lpdf(alpha | 0, .3);
target += normal_lpdf(winB | 1, 1);
target += normal_lpdf(loseB | 1, 1);
target += bernoulli_logit_lpmf(h | alpha + winB * win + loseB * lose);
}
generated quantities{
real alpha_prior;
real winB_prior;
real loseB_prior;
array[trials] int prior_preds;
array[trials] int posterior_preds;
vector[trials] log_lik;
alpha_prior = normal_rng(0, 1);
winB_prior = normal_rng(0, 1);
loseB_prior = normal_rng(0, 1);
prior_preds = bernoulli_rng(inv_logit(winB_prior * win + loseB_prior * lose));
posterior_preds = bernoulli_rng(inv_logit(winB * win + loseB * lose));
for (t in 1:trials){
log_lik[t] = bernoulli_logit_lpmf(h[t] | winB * win + loseB * lose);
}
}
"
write_stan_file(
stan_wsls_model,
dir = "stan/",
basename = "W8_WSLS.stan")
## [1] "/Users/au209589/Dropbox/Teaching/AdvancedCognitiveModeling23_book/stan/W8_WSLS.stan"
file <- file.path("stan/W8_WSLS.stan")
mod_wsls_simple <- cmdstan_model(file,
cpp_options = list(stan_threads = TRUE),
stanc_options = list("O1"),
pedantic = TRUE)
samples_wsls_simple <- mod_wsls_simple$sample(
data = data_wsls_simple,
seed = 123,
chains = 2,
parallel_chains = 2,
threads_per_chain = 2,
iter_warmup = 2000,
iter_sampling = 2000,
refresh = 1000,
max_treedepth = 20,
adapt_delta = 0.99,
)
## Running MCMC with 2 parallel chains, with 2 thread(s) per chain...
##
## Chain 1 Iteration: 1 / 4000 [ 0%] (Warmup)
## Chain 1 Iteration: 1000 / 4000 [ 25%] (Warmup)
## Chain 1 Iteration: 2000 / 4000 [ 50%] (Warmup)
## Chain 1 Iteration: 2001 / 4000 [ 50%] (Sampling)
## Chain 2 Iteration: 1 / 4000 [ 0%] (Warmup)
## Chain 2 Iteration: 1000 / 4000 [ 25%] (Warmup)
## Chain 2 Iteration: 2000 / 4000 [ 50%] (Warmup)
## Chain 2 Iteration: 2001 / 4000 [ 50%] (Sampling)
## Chain 1 Iteration: 3000 / 4000 [ 75%] (Sampling)
## Chain 2 Iteration: 3000 / 4000 [ 75%] (Sampling)
## Chain 1 Iteration: 4000 / 4000 [100%] (Sampling)
## Chain 2 Iteration: 4000 / 4000 [100%] (Sampling)
## Chain 1 finished in 0.6 seconds.
## Chain 2 finished in 0.6 seconds.
##
## Both chains finished successfully.
## Mean chain execution time: 0.6 seconds.
## Total execution time: 0.8 seconds.
8.9 Basic assessment
## # A tibble: 364 × 10
## variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 lp__ -7.23e+1 -7.20e+1 1.26 1.04 -74.8 -71.0 1.00 1537. 2227.
## 2 alpha -5.53e-2 -5.34e-2 0.199 0.200 -0.389 0.265 1.00 2359. 2142.
## 3 winB 1.02e+0 1.01e+0 0.297 0.304 0.547 1.52 1.00 2311. 2347.
## 4 loseB 1.10e+0 1.09e+0 0.322 0.311 0.589 1.65 1.00 2208. 2225.
## 5 alpha_prior -1.14e-2 -5.82e-3 0.983 0.959 -1.66 1.57 1.00 4099. 4033.
## 6 winB_prior -1.96e-2 -2.65e-2 0.999 0.996 -1.63 1.67 1.00 4182. 3530.
## 7 loseB_prior 1.86e-4 -5.70e-3 1.01 1.02 -1.65 1.67 1.00 3953. 3659.
## 8 prior_preds[1] 5.05e-1 1 e+0 0.500 0 0 1 1.00 3797. NA
## 9 prior_preds[2] 5.06e-1 1 e+0 0.500 0 0 1 1.00 4120. NA
## 10 prior_preds[3] 5.08e-1 1 e+0 0.500 0 0 1 1.00 3941. NA
## # ℹ 354 more rows
# Extract posterior samples and include sampling of the prior:
draws_df <- as_draws_df(samples_wsls_simple$draws())
# Now let's plot the density for theta (prior and posterior)
ggplot(draws_df) +
geom_density(aes(alpha), fill = "blue", alpha = 0.3) +
geom_density(aes(alpha_prior), fill = "red", alpha = 0.3) +
geom_vline(xintercept = d_a$alpha[1]) +
xlab("Rate") +
ylab("Posterior Density") +
theme_classic()
ggplot(draws_df) +
geom_density(aes(winB), fill = "blue", alpha = 0.3) +
geom_density(aes(winB_prior), fill = "red", alpha = 0.3) +
geom_vline(xintercept = d_a$betaWin[1]) +
xlab("Rate") +
ylab("Posterior Density") +
theme_classic()
ggplot(draws_df) +
geom_density(aes(loseB), fill = "blue", alpha = 0.3) +
geom_density(aes(loseB_prior), fill = "red", alpha = 0.3) +
geom_vline(xintercept = d_a$betaLose[1]) +
xlab("Rate") +
ylab("Posterior Density") +
theme_classic()
[MISSING: FULL PARAMETER RECOVERY]
8.10 Create multilevel data
## Now multilevel model
d_wsls1 <- d %>% subset(strategy == "WSLS") %>%
subset(select = c(agent, choice)) %>%
mutate(row = row_number()) %>%
pivot_wider(names_from = agent, values_from = choice)
d_wsls2 <- d %>% subset(strategy == "WSLS") %>%
subset(select = c(agent, prevWin)) %>%
mutate(row = row_number()) %>%
pivot_wider(names_from = agent, values_from = prevWin)
d_wsls3 <- d %>% subset(strategy == "WSLS") %>%
subset(select = c(agent, prevLose)) %>%
mutate(row = row_number()) %>%
pivot_wider(names_from = agent, values_from = prevLose)
## Create the data
data_wsls <- list(
trials = trials - 1,
agents = agents,
h = as.matrix(d_wsls1[,2:(agents + 1)]),
win = as.matrix(d_wsls2[,2:(agents + 1)]),
lose = as.matrix(d_wsls3[,2:(agents + 1)])
)
8.11 Create the model
[MISSING: ADD BIAS]
[MISSING: BETTER PREDICTIONS BASED ON 4 SCENARIOS]
stan_wsls_ml_model <- "
functions{
real normal_lb_rng(real mu, real sigma, real lb) {
real p = normal_cdf(lb | mu, sigma); // cdf for bounds
real u = uniform_rng(p, 1);
return (sigma * inv_Phi(u)) + mu; // inverse cdf for value
}
}
// The input (data) for the model.
data {
int<lower = 1> trials;
int<lower = 1> agents;
array[trials, agents] int h;
array[trials, agents] real win;
array[trials, agents] real lose;
}
parameters {
real winM;
real loseM;
vector<lower = 0>[2] tau;
matrix[2, agents] z_IDs;
cholesky_factor_corr[2] L_u;
}
transformed parameters {
matrix[agents,2] IDs;
IDs = (diag_pre_multiply(tau, L_u) * z_IDs)';
}
model {
target += normal_lpdf(winM | 0, 1);
target += normal_lpdf(tau[1] | 0, .3) -
normal_lccdf(0 | 0, .3);
target += normal_lpdf(loseM | 0, .3);
target += normal_lpdf(tau[2] | 0, .3) -
normal_lccdf(0 | 0, .3);
target += lkj_corr_cholesky_lpdf(L_u | 2);
target += std_normal_lpdf(to_vector(z_IDs));
for (i in 1:agents)
target += bernoulli_logit_lpmf(h[,i] | to_vector(win[,i]) * (winM + IDs[i,1]) + to_vector(lose[,i]) * (loseM + IDs[i,2]));
}
generated quantities{
real winM_prior;
real<lower=0> winSD_prior;
real loseM_prior;
real<lower=0> loseSD_prior;
real win_prior;
real lose_prior;
array[trials,agents] int<lower=0, upper = trials> prior_preds;
array[trials,agents] int<lower=0, upper = trials> posterior_preds;
array[trials, agents] real log_lik;
winM_prior = normal_rng(0,1);
winSD_prior = normal_lb_rng(0,0.3,0);
loseM_prior = normal_rng(0,1);
loseSD_prior = normal_lb_rng(0,0.3,0);
win_prior = normal_rng(winM_prior, winSD_prior);
lose_prior = normal_rng(loseM_prior, loseSD_prior);
for (i in 1:agents){
prior_preds[,i] = binomial_rng(trials, inv_logit(to_vector(win[,i]) * (win_prior) + to_vector(lose[,i]) * (lose_prior)));
posterior_preds[,i] = binomial_rng(trials, inv_logit(to_vector(win[,i]) * (winM + IDs[i,1]) + to_vector(lose[,i]) * (loseM + IDs[i,2])));
for (t in 1:trials){
log_lik[t,i] = bernoulli_logit_lpmf(h[t,i] | to_vector(win[,i]) * (winM + IDs[i,1]) + to_vector(lose[,i]) * (loseM + IDs[i,2]));
}
}
}
"
write_stan_file(
stan_wsls_ml_model,
dir = "stan/",
basename = "W8_wsls_ml.stan")
## [1] "/Users/au209589/Dropbox/Teaching/AdvancedCognitiveModeling23_book/stan/W8_wsls_ml.stan"
file <- file.path("stan/W8_wsls_ml.stan")
mod_wsls <- cmdstan_model(file, cpp_options = list(stan_threads = TRUE),
stanc_options = list("O1"),
pedantic = TRUE)
samples_wsls_ml <- mod_wsls$sample(
data = data_wsls,
seed = 123,
chains = 2,
parallel_chains = 2,
threads_per_chain = 2,
iter_warmup = 2000,
iter_sampling = 2000,
refresh = 1000,
max_treedepth = 20,
adapt_delta = 0.99,
)
## Running MCMC with 2 parallel chains, with 2 thread(s) per chain...
##
## Chain 1 Iteration: 1 / 4000 [ 0%] (Warmup)
## Chain 2 Iteration: 1 / 4000 [ 0%] (Warmup)
## Chain 2 Iteration: 1000 / 4000 [ 25%] (Warmup)
## Chain 2 Iteration: 2000 / 4000 [ 50%] (Warmup)
## Chain 2 Iteration: 2001 / 4000 [ 50%] (Sampling)
## Chain 1 Iteration: 1000 / 4000 [ 25%] (Warmup)
## Chain 1 Iteration: 2000 / 4000 [ 50%] (Warmup)
## Chain 1 Iteration: 2001 / 4000 [ 50%] (Sampling)
## Chain 2 Iteration: 3000 / 4000 [ 75%] (Sampling)
## Chain 2 Iteration: 4000 / 4000 [100%] (Sampling)
## Chain 2 finished in 15767.0 seconds.
## Chain 1 Iteration: 3000 / 4000 [ 75%] (Sampling)
## Chain 1 Iteration: 4000 / 4000 [100%] (Sampling)
## Chain 1 finished in 24401.6 seconds.
##
## Both chains finished successfully.
## Mean chain execution time: 20084.3 seconds.
## Total execution time: 24401.7 seconds.
8.12 Quality checks
## # A tibble: 36,115 × 10
## variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 lp__ -5.94e+3 -5.94e+3 13.4 13.6 -5.97e+3 -5.92e+3 1.00 1010. 1927.
## 2 winM 1.55e+0 1.55e+0 0.0360 0.0352 1.49e+0 1.61e+0 1.00 3142. 2977.
## 3 loseM 1.40e+0 1.40e+0 0.0386 0.0384 1.34e+0 1.47e+0 1.00 4214. 2490.
## 4 tau[1] 2.02e-1 2.04e-1 0.0515 0.0494 1.14e-1 2.84e-1 1.00 1254. 1284.
## 5 tau[2] 1.03e-1 9.99e-2 0.0640 0.0750 9.46e-3 2.11e-1 1.00 830. 1457.
## 6 z_IDs[1,… 9.32e-1 9.29e-1 0.840 0.827 -4.35e-1 2.33e+0 1.00 3706. 2847.
## 7 z_IDs[2,… -2.27e-1 -2.29e-1 1.00 0.993 -1.88e+0 1.46e+0 1.00 4178. 2711.
## 8 z_IDs[1,… -8.61e-1 -8.67e-1 0.824 0.821 -2.22e+0 4.73e-1 1.00 4254. 2943.
## 9 z_IDs[2,… -2.54e-1 -2.54e-1 0.986 0.991 -1.86e+0 1.35e+0 1.00 4290. 2965.
## 10 z_IDs[1,… -9.20e-1 -9.42e-1 0.838 0.819 -2.28e+0 4.67e-1 1.00 4330. 3012.
## # ℹ 36,105 more rows
# Extract posterior samples and include sampling of the prior:
draws_df <- as_draws_df(samples_wsls_ml$draws())
# Now let's plot the density for theta (prior and posterior)
ggplot(draws_df) +
geom_density(aes(winM), fill = "blue", alpha = 0.3) +
geom_density(aes(win_prior), fill = "red", alpha = 0.3) +
geom_vline(xintercept = d$betaWinM[1]) +
xlab("Rate") +
ylab("Posterior Density") +
theme_classic()
ggplot(draws_df) +
geom_density(aes(`tau[1]`), fill = "blue", alpha = 0.3) +
geom_density(aes(`winSD_prior`), fill = "red", alpha = 0.3) +
geom_vline(xintercept = d$betaWinSD[1]) +
xlab("Rate") +
ylab("Posterior Density") +
theme_classic()
ggplot(draws_df) +
geom_density(aes(loseM), fill = "blue", alpha = 0.3) +
geom_density(aes(loseM_prior), fill = "red", alpha = 0.3) +
geom_vline(xintercept = d$betaLoseM[1]) +
xlab("Rate") +
ylab("Posterior Density") +
theme_classic()
ggplot(draws_df) +
geom_density(aes(`tau[2]`), fill = "blue", alpha = 0.3) +
geom_density(aes(`loseSD_prior`), fill = "red", alpha = 0.3) +
geom_vline(xintercept = d$betaLoseSD[1]) +
xlab("Rate") +
ylab("Posterior Density") +
theme_classic()
[MISSING: Model comparison with biased] [MISSING: Mixture model with biased]