This vignette shows how to define observed responses with
first_of(), all_of(), none_of(),
and inhibit(). Use these helpers when a response is not a
direct readout from a single accumulator.
An outcome rule states when an observed response should count as having happened. Common cases include:
The logical helpers encode those cases directly in the model definition.
The main helpers are:
first_of(x, y) finishes when the first of
x and y finishesall_of(x, y) finishes when both x and
y have finishednone_of(x) requires x not to have finished
before its enclosing ruleinhibit(x, y) finishes with x unless
y finishes strictly earlierfirst_of(): either route can generate the responseUse first_of() when one of several routes can generate
the same response.
model_first <- race_spec() |>
add_accumulator("word_route", "lognormal") |>
add_accumulator("picture_route", "lognormal") |>
add_outcome("target_detected", first_of("word_route", "picture_route")) |>
finalize_model()
names(model_first$prep$outcomes)## [1] "target_detected"
all_of(): several things must all be trueUse all_of() when a response depends on several
processes all being complete.
model_all <- race_spec() |>
add_accumulator("encoding", "lognormal") |>
add_accumulator("verification", "lognormal") |>
add_outcome("confirmed_response", all_of("encoding", "verification")) |>
finalize_model()
names(model_all$prep$outcomes)## [1] "confirmed_response"
none_of(): the response requires the competing event to
be absentUse none_of() inside all_of() to require
that another event has not occurred before the conjunction
completes.
model_none <- race_spec() |>
add_accumulator("go", "lognormal") |>
add_accumulator("stop", "lognormal") |>
add_outcome("respond", all_of("go", none_of("stop"))) |>
finalize_model()
names(model_none$prep$outcomes)## [1] "respond"
The response occurs when go finishes, provided
stop has not finished earlier.
inhibit(): the response is driven by one process and
blocked by anotherUse inhibit() when one process would generate the
response and another can block it.
model_inhibit <- race_spec() |>
add_accumulator("go", "lognormal") |>
add_accumulator("stop", "lognormal") |>
add_outcome("respond", inhibit("go", "stop")) |>
finalize_model()
names(model_inhibit$prep$outcomes)## [1] "respond"
Here stop cancels the response only if it finishes
before go.
none_of() versus inhibit()These two rules look similar:
rule_none <- all_of("go", none_of("stop"))
rule_inhibit <- inhibit("go", "stop")
c(absence_rule = rule_none$kind, inhibit_rule = rule_inhibit$kind)## absence_rule inhibit_rule
## "and" "guard"
For these two distinct accumulators, they give the same response
rule: go completes before stop. The absence
condition is evaluated when the enclosing conjunction completes, not
over the entire trial.
Nesting matters. all_of("a", "b", none_of("stop"))
checks absence when both a and b have
completed. In all_of(inhibit("a", "stop"), "b"), inhibition
is decided when a completes; a later stop does not undo
that event.
none_of() needs an event at which to evaluate absence.
Use it inside all_of() with an event that generates a
response; a bare none_of() cannot be an outcome or a
standalone branch of first_of().
The helpers can be combined when a response depends on several kinds of logic at once. For example, suppose a response requires:
model_combo <- race_spec() |>
add_accumulator("encoding", "lognormal") |>
add_accumulator("left_route", "lognormal") |>
add_accumulator("right_route", "lognormal") |>
add_accumulator("stop", "lognormal") |>
add_outcome(
"choice",
all_of(
"encoding",
first_of("left_route", "right_route"),
none_of("stop")
)
) |>
finalize_model()
names(model_combo$prep$outcomes)## [1] "choice"