Skip to contents

Generate Stan code for the meta-d' model

Usage

stanvars_metad(
  K,
  distribution = "normal",
  metac_absolute = TRUE,
  categorical = FALSE,
  logit = TRUE
)

Arguments

K

The number of confidence levels

distribution

The noise distribution to use. Should be a parameter-free distribution, i.e., one that is mean-centered without additional variance/shape parameters. If the distribution is not already available in stan, you must additionally provide two functions to Stan (one for <distribution>_lcdf and one for <distribution>_lccdf).

metac_absolute

Should the type 2 criterion (metac) be fixed to the absolute type 1 criterion (c)? If TRUE, the model will set metac = c. Otherwise, it will set metac = M * c, such that the type 2 criterion is relatively equal to the type 1 criterion (i.e., meta_c/meta_dprime = c/dprime)

categorical

If FALSE (default), use the multinomial likelihood over aggregated data. If TRUE, use the categorical likelihood over individual trials.

logit

If TRUE (default), use the logit parameterization of the likelihood over the log joint response probabilities. If FALSE, use the standard parameterization of the likelihood over the actual joint response probabilities. In most cases, the logit parameterization should provide more stable numerical computations, but the standard parameterization might be preferable in some settings.

Value

A brms::stanvar object containing Stan code defining the likelihood for the metad' model with K confidence levels, signal distributed according to the distribution distribution, and where metac = c if metac_absolute==TRUE, and metac = M*c otherwise.

Examples

# create stancode for the meta-d' model
# using the normal distribution and 3 levels of confidence
stanvars_metad(3)
#> [[1]]
#> [[1]]$name
#> [1] ""
#> 
#> [[1]]$sdata
#> NULL
#> 
#> [[1]]$scode
#> [1] "\n  // Convert a binary int x from {0, 1} to {-1, 1}\n  int to_signed(int x) {\n    return 2*x - 1;\n  }\n\n  // P(response, confidence | stimulus) given as simplex\n  // [P(resp=0, conf=K), .... P(resp=0, conf=1), P(resp=1, conf=1), ... P(resp=1, conf=K)]\n  vector metad_normal_pmf(int stimulus, real dprime, real c, real meta_dprime, real meta_c, vector meta_c2_0, vector meta_c2_1) {\n    // number of confidence levels\n    int K = size(meta_c2_0) + 1;\n\n    // type-1 response probabilities\n    real lp_1 = std_normal_lccdf(c - to_signed(stimulus)*dprime/2);\n    real lp_0 = std_normal_lcdf(c - to_signed(stimulus)*dprime/2);\n\n    // means of type-2 distributions\n    real meta_mu = to_signed(stimulus) * meta_dprime/2;\n\n    vector[K] lp2_1;         // CDFs (response == 1)\n    vector[K] lp2_0;         // CDFs (response == 0)\n    vector[2*K] log_theta;   // joint (type-1 x type-2) response probabilities\n\n    lp2_1[1] = std_normal_lccdf(meta_c - meta_mu);\n    lp2_0[1] = std_normal_lcdf(meta_c - meta_mu);\n    for (k in 2:K) {\n      lp2_1[k] = std_normal_lccdf(meta_c2_1[k-1] - meta_mu);\n      lp2_0[k] = std_normal_lcdf(meta_c2_0[k-1] - meta_mu);\n\n      log_theta[K-k+2] = log_diff_exp(lp2_0[k-1], lp2_0[k]);\n      log_theta[K+k-1] = log_diff_exp(lp2_1[k-1], lp2_1[k]);\n    }\n    log_theta[1] = lp2_0[K];\n    log_theta[2*K] = lp2_1[K];\n\n    // weight by P(response|stimulus) and normalize\n    log_theta[1:K] += lp_0 - lp2_0[1];\n    log_theta[(K+1):(2*K)] += lp_1 - lp2_1[1];\n    return log_theta;\n}\n\n  real metad__3__normal__absolute__multinomial_lpmf(array[] int Y, real M, real dprime, real c, real z_meta_c2_0_1, real z_meta_c2_0_2, real z_meta_c2_1_1, real z_meta_c2_1_2) {\n  int K = 3; // number of confidence levels\n\n  real meta_dprime = M * dprime;\n  real meta_c = c;\n  vector[K-1] meta_c2_0 = meta_c - cumulative_sum([z_meta_c2_0_1, z_meta_c2_0_2]');\n  vector[K-1] meta_c2_1 = meta_c + cumulative_sum([z_meta_c2_1_1, z_meta_c2_1_2]');\n\n  // use multinomial likelihood\n  return multinomial_logit_lpmf(Y[1:(2*K)] | metad_normal_pmf(0, dprime, c,\n                          meta_dprime, meta_c, meta_c2_0, meta_c2_1)) +\n    multinomial_logit_lpmf(Y[(2*K+1):(4*K)] |  metad_normal_pmf(1, dprime, c,\n                      meta_dprime, meta_c, meta_c2_0, meta_c2_1));\n}"
#> 
#> [[1]]$block
#> [1] "functions"
#> 
#> [[1]]$position
#> [1] "start"
#> 
#> [[1]]$pll_args
#> character(0)
#> 
#> 
#> attr(,"class")
#> [1] "stanvars"

# create stancode for the meta-d' model with meta_c = M * c
stanvars_metad(3, metac_absolute = FALSE)
#> [[1]]
#> [[1]]$name
#> [1] ""
#> 
#> [[1]]$sdata
#> NULL
#> 
#> [[1]]$scode
#> [1] "\n  // Convert a binary int x from {0, 1} to {-1, 1}\n  int to_signed(int x) {\n    return 2*x - 1;\n  }\n\n  // P(response, confidence | stimulus) given as simplex\n  // [P(resp=0, conf=K), .... P(resp=0, conf=1), P(resp=1, conf=1), ... P(resp=1, conf=K)]\n  vector metad_normal_pmf(int stimulus, real dprime, real c, real meta_dprime, real meta_c, vector meta_c2_0, vector meta_c2_1) {\n    // number of confidence levels\n    int K = size(meta_c2_0) + 1;\n\n    // type-1 response probabilities\n    real lp_1 = std_normal_lccdf(c - to_signed(stimulus)*dprime/2);\n    real lp_0 = std_normal_lcdf(c - to_signed(stimulus)*dprime/2);\n\n    // means of type-2 distributions\n    real meta_mu = to_signed(stimulus) * meta_dprime/2;\n\n    vector[K] lp2_1;         // CDFs (response == 1)\n    vector[K] lp2_0;         // CDFs (response == 0)\n    vector[2*K] log_theta;   // joint (type-1 x type-2) response probabilities\n\n    lp2_1[1] = std_normal_lccdf(meta_c - meta_mu);\n    lp2_0[1] = std_normal_lcdf(meta_c - meta_mu);\n    for (k in 2:K) {\n      lp2_1[k] = std_normal_lccdf(meta_c2_1[k-1] - meta_mu);\n      lp2_0[k] = std_normal_lcdf(meta_c2_0[k-1] - meta_mu);\n\n      log_theta[K-k+2] = log_diff_exp(lp2_0[k-1], lp2_0[k]);\n      log_theta[K+k-1] = log_diff_exp(lp2_1[k-1], lp2_1[k]);\n    }\n    log_theta[1] = lp2_0[K];\n    log_theta[2*K] = lp2_1[K];\n\n    // weight by P(response|stimulus) and normalize\n    log_theta[1:K] += lp_0 - lp2_0[1];\n    log_theta[(K+1):(2*K)] += lp_1 - lp2_1[1];\n    return log_theta;\n}\n\n  real metad__3__normal__relative__multinomial_lpmf(array[] int Y, real M, real dprime, real c, real z_meta_c2_0_1, real z_meta_c2_0_2, real z_meta_c2_1_1, real z_meta_c2_1_2) {\n  int K = 3; // number of confidence levels\n\n  real meta_dprime = M * dprime;\n  real meta_c = M * c;\n  vector[K-1] meta_c2_0 = meta_c - cumulative_sum([z_meta_c2_0_1, z_meta_c2_0_2]');\n  vector[K-1] meta_c2_1 = meta_c + cumulative_sum([z_meta_c2_1_1, z_meta_c2_1_2]');\n\n  // use multinomial likelihood\n  return multinomial_logit_lpmf(Y[1:(2*K)] | metad_normal_pmf(0, dprime, c,\n                          meta_dprime, meta_c, meta_c2_0, meta_c2_1)) +\n    multinomial_logit_lpmf(Y[(2*K+1):(4*K)] |  metad_normal_pmf(1, dprime, c,\n                      meta_dprime, meta_c, meta_c2_0, meta_c2_1));\n}"
#> 
#> [[1]]$block
#> [1] "functions"
#> 
#> [[1]]$position
#> [1] "start"
#> 
#> [[1]]$pll_args
#> character(0)
#> 
#> 
#> attr(,"class")
#> [1] "stanvars"

# create stancode for the meta-d' model with
# an alternative distribution
# note: cumulative distribution functions must be defined
# in R and in Stan using [brms::stanvar()]
stanvars_metad(4, distribution = "gumbel_min")
#> [[1]]
#> [[1]]$name
#> [1] ""
#> 
#> [[1]]$sdata
#> NULL
#> 
#> [[1]]$scode
#> [1] "\n  // Convert a binary int x from {0, 1} to {-1, 1}\n  int to_signed(int x) {\n    return 2*x - 1;\n  }\n\n  // P(response, confidence | stimulus) given as simplex\n  // [P(resp=0, conf=K), .... P(resp=0, conf=1), P(resp=1, conf=1), ... P(resp=1, conf=K)]\n  vector metad_gumbel_min_pmf(int stimulus, real dprime, real c, real meta_dprime, real meta_c, vector meta_c2_0, vector meta_c2_1) {\n    // number of confidence levels\n    int K = size(meta_c2_0) + 1;\n\n    // type-1 response probabilities\n    real lp_1 = gumbel_min_lccdf(c | to_signed(stimulus)*dprime/2);\n    real lp_0 = gumbel_min_lcdf(c | to_signed(stimulus)*dprime/2);\n\n    // means of type-2 distributions\n    real meta_mu = to_signed(stimulus) * meta_dprime/2;\n\n    vector[K] lp2_1;         // CDFs (response == 1)\n    vector[K] lp2_0;         // CDFs (response == 0)\n    vector[2*K] log_theta;   // joint (type-1 x type-2) response probabilities\n\n    lp2_1[1] = gumbel_min_lccdf(meta_c | meta_mu);\n    lp2_0[1] = gumbel_min_lcdf(meta_c | meta_mu);\n    for (k in 2:K) {\n      lp2_1[k] = gumbel_min_lccdf(meta_c2_1[k-1] | meta_mu);\n      lp2_0[k] = gumbel_min_lcdf(meta_c2_0[k-1] | meta_mu);\n\n      log_theta[K-k+2] = log_diff_exp(lp2_0[k-1], lp2_0[k]);\n      log_theta[K+k-1] = log_diff_exp(lp2_1[k-1], lp2_1[k]);\n    }\n    log_theta[1] = lp2_0[K];\n    log_theta[2*K] = lp2_1[K];\n\n    // weight by P(response|stimulus) and normalize\n    log_theta[1:K] += lp_0 - lp2_0[1];\n    log_theta[(K+1):(2*K)] += lp_1 - lp2_1[1];\n    return log_theta;\n}\n\n  real metad__4__gumbel_min__absolute__multinomial_lpmf(array[] int Y, real M, real dprime, real c, real z_meta_c2_0_1, real z_meta_c2_0_2, real z_meta_c2_0_3, real z_meta_c2_1_1, real z_meta_c2_1_2, real z_meta_c2_1_3) {\n  int K = 4; // number of confidence levels\n\n  real meta_dprime = M * dprime;\n  real meta_c = c;\n  vector[K-1] meta_c2_0 = meta_c - cumulative_sum([z_meta_c2_0_1, z_meta_c2_0_2, z_meta_c2_0_3]');\n  vector[K-1] meta_c2_1 = meta_c + cumulative_sum([z_meta_c2_1_1, z_meta_c2_1_2, z_meta_c2_1_3]');\n\n  // use multinomial likelihood\n  return multinomial_logit_lpmf(Y[1:(2*K)] | metad_gumbel_min_pmf(0, dprime, c,\n                          meta_dprime, meta_c, meta_c2_0, meta_c2_1)) +\n    multinomial_logit_lpmf(Y[(2*K+1):(4*K)] |  metad_gumbel_min_pmf(1, dprime, c,\n                      meta_dprime, meta_c, meta_c2_0, meta_c2_1));\n}"
#> 
#> [[1]]$block
#> [1] "functions"
#> 
#> [[1]]$position
#> [1] "start"
#> 
#> [[1]]$pll_args
#> character(0)
#> 
#> 
#> attr(,"class")
#> [1] "stanvars"