| Title: | Simulation and Likelihood Tools for Evidence-Accumulation Models |
|---|---|
| Description: | Toolbox for designing, simulating and fitting custom racing evidence-accumulation models to behavioral data. Users define a model in terms of accumulators, pooled channels, logical response rules, and timing dependencies. This allows more direct embedding of psychological hypotheses into the model. With that model you can simulate decision-making data and evaluate the likelihood of observed responses under alternative parameter values. The user-facing interface stays in R, while the simulation and likelihood calculations are handled in C++ for speed. |
| Authors: | Niek Stevenson [aut, cre] |
| Maintainer: | Niek Stevenson <[email protected]> |
| License: | GPL-3 |
| Version: | 1.0.0 |
| Built: | 2026-10-22 19:26:34 UTC |
| Source: | https://github.com/cran/AccumulatR |
AccumulatR is for researchers who work with response-time models in which
behavior is generated by competing or interacting accumulators. The package
lets you describe a model in psychologically meaningful terms, simulate
behavioral data, and evaluate likelihoods for fitting.
The main workflow is:
Start with race_spec().
Add accumulators, pools, outcomes, and timing relations.
Call finalize_model() and supply values with build_param_matrix().
Generate observations with simulate(), or use experimental data.
Call prepare_data() and make_context(), then evaluate candidate
parameters with log_likelihood().
See vignette("getting_started", package = "AccumulatR") for a worked example.
Maintainer: Niek Stevenson [email protected]
Useful links:
Report bugs at https://github.com/niekstevenson/AccumulatR/issues
An accumulator has a random finishing time drawn from dist, shifted by
its onset and nondecision time t0. Use add_outcome() to connect its
completion to an observed response.
add_accumulator(spec, id, dist, onset = 0)add_accumulator(spec, id, dist, onset = 0)
spec |
A |
id |
Label for the accumulator. |
dist |
Distribution family: |
onset |
Start time for the accumulator. This can be a fixed numeric
onset or a chained onset created with |
The updated race_spec.
set_parameters(), build_param_matrix(), after()
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal")spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal")
Components are useful when trials can come from qualitatively different
processing modes, such as fast versus slow processing. Component declarations
define membership only; component probabilities are configured with
set_mixture().
add_component(spec, id, members, n_outcomes = NULL)add_component(spec, id, members, n_outcomes = NULL)
spec |
A |
id |
Component label. |
members |
Accumulator labels that belong to this component. |
n_outcomes |
Optional component-specific override for the number of observed ordered responses. |
The updated race_spec.
Attach a response label to a finishing rule. In a single-response model, the first available outcome determines the observed response and time.
add_outcome(spec, label, expr, options = list())add_outcome(spec, label, expr, options = list())
spec |
A |
label |
Response label that should appear in the behavioral data. |
expr |
Accumulator or pool label, or a rule built with |
options |
Named list of response settings:
Guessing and remapping require |
The updated race_spec.
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A")spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A")
A pool finishes when its kth member finishes. Use the pool label in
add_outcome(), another pool, or after().
add_pool(spec, id, members, k = 1L)add_pool(spec, id, members, k = 1L)
spec |
A |
id |
Label for the pool. |
members |
Accumulator or pool labels included in the pool. |
k |
Number of members that must finish, from |
The updated race_spec.
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_accumulator(spec, "B", "lognormal") spec <- add_pool(spec, "P1", members = c("A", "B"), k = 1L)spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_accumulator(spec, "B", "lognormal") spec <- add_pool(spec, "P1", members = c("A", "B"), k = 1L)
With probability given by name, all member accumulators are absent on a
trial. Otherwise they follow their specified onsets and finishing-time
distributions. Use separate triggers for independent absence events;
set_parameters() can give those events a common probability.
add_trigger(spec, name, members)add_trigger(spec, name, members)
spec |
A |
name |
Trigger parameter name. Supply its probability in |
members |
Accumulator labels controlled by the shared absence draw. |
The updated race_spec.
This is useful for staged or contingent architectures, where one process can only begin after an earlier accumulator or pool has finished.
after(source, lag = 0)after(source, lag = 0)
source |
Accumulator or pool label that must finish first. |
lag |
Optional non-negative delay added after |
A chained-onset specification for add_accumulator(onset = ...).
after("A") after("pool1", lag = 0.05)after("A") after("pool1", lag = 0.05)
Return a rule that finishes when all required events have completed.
Any none_of() conditions are checked at that finishing time.
all_of(...)all_of(...)
... |
Accumulator labels, pool labels, or response expressions. |
An expression object.
all_of("A", "B")all_of("A", "B")
Convert a source label or quoted R expression into a response rule for
add_outcome(). Within a quoted expression, & combines requirements,
| allows alternative routes, and ! specifies an absence condition.
These correspond to all_of(), first_of(), and none_of().
build_outcome_expr(expr)build_outcome_expr(expr)
expr |
Accumulator or pool label, symbol, quoted logical expression, or an expression object returned by an outcome helper. |
An expression object used inside model specifications.
build_outcome_expr(quote(A & !B))build_outcome_expr(quote(A & !B))
Expand a named parameter vector into the matrix used by simulate() and
log_likelihood(). Each trial receives the same parameter values; rows are
grouped by trial, with accumulators in their model declaration order.
build_param_matrix(model, param_values, n_trials = 1L)build_param_matrix(model, param_values, n_trials = 1L)
model |
Finalized model structure. |
param_values |
Named numeric vector using the names from |
n_trials |
Positive integer number of trial blocks to create. |
Parameter domains are checked before expansion. All values must be
finite, t0 must be nonnegative, and distribution scales must be positive.
Trigger probabilities must lie in [0, 1]; sampled mixture weights must be
nonnegative and sum to at most one. See
vignette("distributions", package = "AccumulatR") for distribution-specific
constraints.
For trial-specific parameters, modify the relevant matrix rows while preserving their order and column layout. Likelihood and simulation calls use those values directly, so modified values must satisfy the same domains.
A numeric matrix with one row per trial/accumulator pair. Columns
contain trigger absence probability q, nondecision time t0, distribution
parameters p1, p2, and so on, followed by sampled mixture weights.
Distribution slots follow the order listed in the Supported Distributions
vignette. Accumulators without a trigger have q = 0.
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") vals <- c(m = 0, s = 0.1) build_param_matrix(finalize_model(spec), vals, n_trials = 2)spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") vals <- c(m = 0, s = 0.1) build_param_matrix(finalize_model(spec), vals, n_trials = 2)
Report how many symbolic regions, numerical operations, and integration kernels the model requires. Use these counts to investigate models whose context construction or likelihood evaluation is expensive.
complexity_metrics(context)complexity_metrics(context)
context |
Context created with |
A list containing a variants data frame and a total list.
Each variant is a compiled component plan. Columns count symbolic regions
and cells, compiled roots and nodes, and integral kernels. Fields beginning
with max_ report maxima; other total fields sum across variants.
Check source references, timing dependencies, response rules, and parameter
grouping, and create the model object used by simulate(), prepare_data(),
and build_param_matrix(). Build its likelihood context with make_context().
finalize_model(model)finalize_model(model)
model |
Model specification. |
A model_structure object.
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") finalize_model(spec)spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") finalize_model(spec)
Return a rule whose finishing time is the earliest completion among its arguments. Each argument must contain an event that can generate a response.
first_of(...)first_of(...)
... |
Accumulator labels, pool labels, or response expressions. |
An expression object.
first_of("A", "B")first_of("A", "B")
The response occurs when reference finishes, provided by has not
finished strictly earlier. A blocker that finishes later does not cancel
a response that has already occurred.
inhibit(reference, by)inhibit(reference, by)
reference |
Response rule, accumulator label, or pool label to be blocked. |
by |
Blocking process or expression. |
A guarded expression object.
inhibit("A", "B")inhibit("A", "B")
Compute the summed log-likelihood by default, or trial-wise log-likelihoods
when sum = FALSE.
Build the context and prepare the observations once, then reuse them when
evaluating candidate parameter matrices for the same model.
log_likelihood( context, data, parameters, ok = NULL, sum = TRUE, min_ll = log(1e-10) )log_likelihood( context, data, parameters, ok = NULL, sum = TRUE, min_ll = log(1e-10) )
context |
Context created with |
data |
Prepared data created with |
parameters |
Numeric matrix from |
ok |
Optional logical vector with one value per prepared trial.
|
sum |
If |
min_ll |
Minimum log-likelihood value used for excluded or impossible trials. |
Response-time observations contribute densities, so a log-likelihood can be positive. Missing responses and censoring contribute probability masses according to the observation rules.
Use prepare_data() and build_param_matrix() for the same model.
Evaluation assumes matching layouts and valid parameters and does not
repeat preparation checks.
A summed log-likelihood by default, or a numeric vector of
trial-wise log-likelihood values when sum = FALSE.
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") structure <- finalize_model(spec) params_df <- build_param_matrix( structure, c(m = 0, s = 0.1), n_trials = 2 ) data_df <- simulate(structure, params_df, seed = 1) prepared <- prepare_data(structure, data_df) ctx <- make_context(structure) log_likelihood(ctx, prepared, params_df)spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") structure <- finalize_model(spec) params_df <- build_param_matrix( structure, c(m = 0, s = 0.1), n_trials = 2 ) data_df <- simulate(structure, params_df, seed = 1) prepared <- prepare_data(structure, data_df) ctx <- make_context(structure) log_likelihood(ctx, prepared, params_df)
Compile the model's response rules and dependencies for likelihood
evaluation. Reuse the context across candidate parameter values and datasets
for the same model. Prepare each dataset with prepare_data().
make_context(structure, diagnostics = FALSE)make_context(structure, diagnostics = FALSE)
structure |
Finalized model structure. |
diagnostics |
If |
An accumulatr_context object.
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") structure <- finalize_model(spec) make_context(structure)spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") structure <- finalize_model(spec) make_context(structure)
Use inside all_of() to require that expr has not finished before the
conjunction completes. Absence supplies a condition, not a response time,
so it cannot be an outcome on its own or a standalone first_of() branch.
none_of(expr)none_of(expr)
expr |
Accumulator label, pool label, or expression to negate. |
An expression object.
all_of("go", none_of("stop"))all_of("go", none_of("stop"))
Return the names accepted by build_param_matrix(), after applying any
grouping or renaming in set_parameters(). The list includes nondecision
times, trigger probabilities, and sampled mixture weights where applicable.
These names do not determine which parameters an optimizer must estimate;
parameters can be held fixed by supplying constant values.
par_names(model)par_names(model)
model |
A |
A character vector of parameter names.
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") par_names(spec)spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") par_names(spec)
Draw a schematic view of the model with time on the x-axis and evidence on the y-axis. Each accumulator is shown as a simple trajectory, and timing or blocking relations are overlaid so you can inspect the qualitative structure of the model before fitting.
plot_accumulators( model, xlim = NULL, ylim = c(0, 1), line_length = 1, angle_dodge = 10, accumulator_order = NULL, curve_strength = 0.18, line_col = "gray20", line_lwd = 3, axis_lwd = 3, blocker_lwd = 2.6, show_labels = TRUE, main = "Accumulator Schematic", xlab = "Time", ylab = "Evidence", ... )plot_accumulators( model, xlim = NULL, ylim = c(0, 1), line_length = 1, angle_dodge = 10, accumulator_order = NULL, curve_strength = 0.18, line_col = "gray20", line_lwd = 3, axis_lwd = 3, blocker_lwd = 2.6, show_labels = TRUE, main = "Accumulator Schematic", xlab = "Time", ylab = "Evidence", ... )
model |
A finalized model structure. |
xlim |
Optional x-axis limits. If |
ylim |
Y-axis limits. |
line_length |
Length of each accumulator trajectory in plot units. |
angle_dodge |
Target angular spacing in degrees between neighbouring accumulators. Angles are centered at 45 degrees and constrained to 10 to 80 degrees. If there are too many accumulators sharing the same onset, spacing is reduced uniformly within that onset group. |
accumulator_order |
Optional character vector of accumulator labels, ordered from top-to-bottom. This only affects accumulators sharing the same onset. |
curve_strength |
Curvature applied to relationship arcs. |
line_col |
Color for accumulator trajectories. |
line_lwd |
Line width for accumulator trajectories. |
axis_lwd |
Line width for x/y axis arrows. |
blocker_lwd |
Line width for blocker arrows. |
show_labels |
If |
main |
Main title. |
xlab |
X-axis label. |
ylab |
Y-axis label. |
... |
Additional arguments passed to |
Invisibly returns a list with plotting coordinates and inferred structural links.
spec <- race_spec() |> add_accumulator("go", "lognormal") |> add_accumulator("stop", "lognormal", onset = 0.15) |> add_pool("P", c("go", "stop")) |> add_outcome("R", "P") |> finalize_model() plot_accumulators(spec)spec <- race_spec() |> add_accumulator("go", "lognormal") |> add_accumulator("stop", "lognormal", onset = 0.15) |> add_pool("P", c("go", "stop")) |> add_outcome("R", "P") |> finalize_model() plot_accumulators(spec)
Check response labels, times, and observation conditions, and arrange the
data into one row per accumulator per trial for log_likelihood().
prepare_data(structure, data_df, compress = FALSE)prepare_data(structure, data_df, compress = FALSE)
structure |
Finalized model structure. |
data_df |
Data frame with response labels |
compress |
If |
Trial layout. Without a racer column, each row is one trial. Trials
are numbered consecutively in input order. To supply accumulator-specific
onsets, include trials and racer columns with one complete accumulator
block per trial in model order. Repeat each trial's response and component
values across its block. An onset replaces a fixed onset or adds an offset
to a chained onset. Omit it to use model defaults.
Mixtures. A nonmissing component label conditions on that component.
An omitted or NA label averages over the model's mixture probabilities.
Missing observations. For single-response models, R = NA and rt = NA
denote no observed response. A finite rt requires a response label.
A known response with rt = NA requires a censoring code or a model with
an observation rule such as guess or map_outcome_to.
Ranked responses. Supply paired columns R2/rt2, R3/rt3, and so
on. The first pair must be observed. Labels cannot repeat, and observed
times must increase strictly. Later pairs may both be NA; all subsequent
ranks must then also be missing. Ranked observations support neither
censoring/truncation nor guessing/remapping rules.
Censoring and truncation. LT and UT define the observation window.
An active truncation window conditions the likelihood on an observable
response with . For a censored trial set rt = NA
and use:
missingness = 1 for ;
missingness = 2 for ;
missingness = 3 for the union of those intervals.
R may retain a known response or be NA. Uncensored trials use
missingness = NA. Missing bounds default to LT = LC = 0 and
UC = UT = Inf. These bounds describe the recorded response time.
Times exactly at LC or UC remain uncensored.
A data frame of class accumulatr_data, with accumulator rows grouped
by trial and ordered as in the model. Response and component labels are
factors with model-defined levels; attributes store the likelihood layout.
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") structure <- finalize_model(spec) params_df <- build_param_matrix( structure, c(m = 0, s = 0.1), n_trials = 2 ) data_df <- simulate(structure, params_df, seed = 1) prepare_data(structure, data_df)spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") structure <- finalize_model(spec) params_df <- build_param_matrix( structure, c(m = 0, s = 0.1), n_trials = 2 ) data_df <- simulate(structure, params_df, seed = 1) prepare_data(structure, data_df)
Show the observed responses, the accumulators or pools that feed them, and any blocking relationships as a directed graph.
processing_tree(model, outcome_label = NULL, return_dot = FALSE)processing_tree(model, outcome_label = NULL, return_dot = FALSE)
model |
A finalized model structure. |
outcome_label |
Optional response label. If supplied, only that response is shown. |
return_dot |
If |
If DiagrammeR is available and return_dot = FALSE, a
DiagrammeR graph. Otherwise, a list with dot, nodes, and edges.
Create an empty specification. Add accumulators and response rules, then
call finalize_model() before simulation or likelihood evaluation.
race_spec(n_outcomes = 1L)race_spec(n_outcomes = 1L)
n_outcomes |
Number of ordered observed responses to retain per trial.
Use |
A race_spec object.
race_spec()race_spec()
Calculate the probability of each first response, integrated over response
time and averaged over mixture components. With multiple trial blocks in
parameters, return the mean probabilities across those blocks.
response_probabilities(context, parameters, include_na = TRUE)response_probabilities(context, parameters, include_na = TRUE)
context |
Context created with |
parameters |
Parameter matrix from |
include_na |
If |
A named numeric vector of marginal response probabilities. Names are
observed outcome labels. When include_na = TRUE, a residual "NA" entry
is included if the model assigns probability mass to unobserved or
NA-mapped outcomes.
spec <- race_spec() |> add_accumulator("left", "lognormal") |> add_accumulator("right", "lognormal") |> add_outcome("left", "left") |> add_outcome("right", "right") |> set_parameters(separate = list(m = TRUE)) model <- finalize_model(spec) params <- build_param_matrix( model, c(left.m = log(0.25), right.m = log(0.40), s = 0.20), n_trials = 1 ) response_probabilities(make_context(model), params)spec <- race_spec() |> add_accumulator("left", "lognormal") |> add_accumulator("right", "lognormal") |> add_outcome("left", "left") |> add_outcome("right", "right") |> set_parameters(separate = list(m = TRUE)) model <- finalize_model(spec) params <- build_param_matrix( model, c(left.m = log(0.25), right.m = log(0.40), s = 0.20), n_trials = 1 ) response_probabilities(make_context(model), params)
Fixed mixtures use known component probabilities. Sampled mixtures expose
automatic p.<component> parameters for every non-reference component; the
reference component receives the residual probability.
Both modes draw a component during simulation. In likelihood evaluation,
a supplied component label conditions on that component; an absent or NA
label averages over components using their probabilities.
set_mixture( spec, mode = c("fixed", "sample"), weights = NULL, reference = NULL )set_mixture( spec, mode = c("fixed", "sample"), weights = NULL, reference = NULL )
spec |
A |
mode |
|
weights |
Named numeric component probabilities for fixed mixtures. If
|
reference |
Reference component for sampled mixtures. Its probability is one minus the sum of non-reference probabilities. Defaults to the last component added to the model. |
The updated race_spec.
Parameters are grouped by compatible type by default. For example, two
lognormal accumulators expose one m, one s, and one t0 parameter unless
you ask for specific parameters to be separate. Triggers expose their trigger name
directly, and sampled mixtures expose automatic p.<component> parameters.
set_parameters(spec, separate = NULL, share = NULL, rename = NULL)set_parameters(spec, separate = NULL, share = NULL, rename = NULL)
spec |
A |
separate |
Named list. Each name is a grouped public parameter, and each
value is one or more accumulator ids to split from that group. Use |
share |
Named list mapping a new public name to default public names or internal parameter names that should share one value. |
rename |
Named character vector mapping current public names to new public names. |
The updated race_spec.
spec <- race_spec() |> add_accumulator("go", "lognormal") |> add_accumulator("stop", "lognormal") |> add_outcome("go", "go") |> add_outcome("stop", "stop") |> set_parameters( separate = list(m = c("go", "stop")), rename = c(s = "spread", t0 = "nondecision") ) par_names(spec)spec <- race_spec() |> add_accumulator("go", "lognormal") |> add_accumulator("stop", "lognormal") |> add_outcome("go", "go") |> add_outcome("stop", "stop") |> set_parameters( separate = list(m = c("go", "stop")), rename = c(s = "spread", t0 = "nondecision") ) par_names(spec)
Generate one trial for each accumulator block in params_df, using the
model's response rules, timing dependencies, triggers, and mixture weights.
simulate( structure, params_df, trial_df = NULL, seed = NULL, keep_detail = FALSE, keep_component = NULL )simulate( structure, params_df, trial_df = NULL, seed = NULL, keep_detail = FALSE, keep_component = NULL )
structure |
Finalized model structure. |
params_df |
Parameter matrix from |
trial_df |
Optional trial-by-accumulator conditioning table containing
complete |
seed |
Optional seed passed to |
keep_detail |
If |
keep_component |
Whether to keep the chosen mixture component in the
output when the model has multiple components. If |
Use the same model to build params_df and simulate the data.
Supply valid parameter values and keep the matrix and conditioning table
in matching row order; simulation does not check their layout or domains.
A data frame with one row per trial and columns trials, R, and
rt. A trial with no observed response has R = NA and rt = NA.
If n_outcomes > 1, additional ordered response pairs such as R2/rt2
are included; unobserved later ranks are NA.
prepare_data(), log_likelihood(), set_mixture()
spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") structure <- finalize_model(spec) params <- c(m = 0, s = 0.1) df <- build_param_matrix(structure, params, n_trials = 3) simulate(structure, df, seed = 123)spec <- race_spec() spec <- add_accumulator(spec, "A", "lognormal") spec <- add_outcome(spec, "A_win", "A") structure <- finalize_model(spec) params <- c(m = 0, s = 0.1) df <- build_param_matrix(structure, params, n_trials = 3) simulate(structure, df, seed = 123)