| Title: | Anytime-Valid Sequential Benchmarking of Algorithms |
|---|---|
| Description: | Sequential, anytime-valid comparison of algorithms on paired losses with a practical-equivalence margin. Confidence sequences for the mean paired difference, superiority/equivalence/continue decisions, cost accounting and auditable trajectories. |
| Authors: | André Leite [aut, cre] (ORCID: <https://orcid.org/0000-0002-4718-9766>), Raydonal Ospina [aut] (ORCID: <https://orcid.org/0000-0002-9884-9090>), Cristiano Ferraz [aut] (ORCID: <https://orcid.org/0000-0002-6838-6734>) |
| Maintainer: | André Leite <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.1.0 |
| Built: | 2026-10-09 16:53:40 UTC |
| Source: | https://github.com/castlaboratory/seqbench |
Running-intersection confidence sequence for the mean paired difference
against the number of instances, with the equivalence band
[-margin, margin] and the zero line.
autoplot.seqbench_comparison(object, ...)autoplot.seqbench_comparison(object, ...)
object |
A |
... |
Unused. |
A ggplot object.
design <- comparison_design(margin = 0.05, bounds = c(0, 1), boundary = "hoeffding") set.seed(2) losses <- data.frame(instance = 1:40, loss_a = runif(40, 0, .5), loss_b = runif(40, .2, .7)) state <- update_comparison(initialize_comparison(design), losses) ggplot2::autoplot(state)design <- comparison_design(margin = 0.05, bounds = c(0, 1), boundary = "hoeffding") set.seed(2) losses <- data.frame(instance = 1:40, loss_a = runif(40, 0, .5), loss_b = runif(40, .2, .7)) state <- update_comparison(initialize_comparison(design), losses) ggplot2::autoplot(state)
Fixes, before any data is seen, everything the procedure needs: the error
level, the margin of practical equivalence, the loss bounds, the boundary and
the budget. The design is immutable; initialize_comparison() turns it into
a state that update_comparison() advances.
comparison_design( alpha = 0.05, margin, bounds, boundary = "betting", paired = TRUE, cost_per_round = 1, budget = Inf, n_max = Inf, betting_c = 0.5, betting_theta = 0.5, betting_grid = 1001L, sd_max = NULL )comparison_design( alpha = 0.05, margin, bounds, boundary = "betting", paired = TRUE, cost_per_round = 1, budget = Inf, n_max = Inf, betting_c = 0.5, betting_theta = 0.5, betting_grid = 1001L, sd_max = NULL )
alpha |
Miscoverage level of the confidence sequence, in (0, 1). The
probability that the procedure ever issues a false declaration (of any of
the three kinds) is at most |
margin |
Margin of practical equivalence |
bounds |
Known bounds |
boundary |
One of |
paired |
Must be |
cost_per_round |
Default cost of one |
budget |
Total cost after which the procedure stops with an
inconclusive outcome. |
n_max |
Maximum number of instances. |
betting_c, betting_theta, betting_grid
|
Tuning of the betting boundary:
truncation constant of the bets (default 1/2, the value recommended by
Waudby-Smith & Ramdas, who also suggest 3/4), hedging weight (default
1/2) and grid size on |
sd_max |
Required for |
An object of class seqbench_design (a list).
Waudby-Smith, I. and Ramdas, A. (2024). Estimating means of bounded random variables by betting. Journal of the Royal Statistical Society Series B, 86(1), 1-27. doi:10.1093/jrsssb/qkad009
initialize_comparison(), planning_horizon()
design <- comparison_design(alpha = 0.05, margin = 0.02, bounds = c(0, 1)) designdesign <- comparison_design(alpha = 0.05, margin = 0.02, bounds = c(0, 1)) design
Returns every element the protocol promises: estimand, estimate, current
confidence sequence, required assumptions, diagnostics, decision and
stopping reason, resources consumed, seeds and versions. summary() is an
alias.
comparison_report(state) ## S3 method for class 'seqbench_comparison' summary(object, ...)comparison_report(state) ## S3 method for class 'seqbench_comparison' summary(object, ...)
state |
A |
object |
A |
... |
Unused. |
A list of class seqbench_report.
state <- initialize_comparison(comparison_design(margin = 0.02, bounds = c(0, 1))) comparison_report(state)state <- initialize_comparison(comparison_design(margin = 0.02, bounds = c(0, 1))) comparison_report(state)
Glance at a comparison
## S3 method for class 'seqbench_comparison' glance(x, ...)## S3 method for class 'seqbench_comparison' glance(x, ...)
x |
A |
... |
Unused. |
A one-row tibble: boundary, valid, alpha, margin,
n_instances, n_evaluations, cost, estimate, lower, upper,
decision, stopping_reason.
state <- initialize_comparison(comparison_design(margin = 0.02, bounds = c(0, 1))) glance(state)state <- initialize_comparison(comparison_design(margin = 0.02, bounds = c(0, 1))) glance(state)
Creates the state object that update_comparison() advances one instance
at a time. Nothing is observed yet; the confidence sequence is [a, b], the
full range of the paired difference.
initialize_comparison(design)initialize_comparison(design)
design |
An object of class seqbench_comparison. Its main components are
design, data (all rows seen so far), trajectory (one row per
instance: estimate, confidence sequence, running intersection, cost and
decision at that time), decision, stopping_reason, diagnostics and
meta (versions, timestamps). Use tidy() for the trajectory, glance()
for a one-row summary and comparison_report() for the full contract.
design <- comparison_design(margin = 0.02, bounds = c(0, 1)) state <- initialize_comparison(design) statedesign <- comparison_design(margin = 0.02, bounds = c(0, 1)) state <- initialize_comparison(design) state
Number of instances after which the confidence sequence is expected to have
declared the true hypothesis, when the true mean paired difference is at
distance distance from the nearest threshold -margin or margin. Two
modes:
planning_horizon(design, distance, sd = NULL, max_t = 1e+06)planning_horizon(design, distance, sd = NULL, max_t = 1e+06)
design |
|
distance |
Positive distance |
sd |
Optional standard deviation of the paired difference, loss units. |
max_t |
Search limit. |
sd = NULL (default): the guaranteed horizon
t*(Delta) = min{t : 2 w_t < |Delta|} of the predictable-plug-in
Hoeffding boundary, whose half-width w_t is deterministic. On the
coverage event (probability at least 1 - alpha) the Hoeffding comparison
has stopped with the correct declaration by then (theory note, Prop. 2).
It is distribution-free and, for small margins, very conservative: it can
exceed what the default betting boundary needs by two or three orders of
magnitude.
sd given (standard deviation of the paired difference, loss units): an
approximation that plugs sd into the empirical-Bernstein half-width
in place of the running variance estimate. It is not a bound; it is the
order of magnitude a variance-adaptive boundary needs, and the betting
boundary is usually faster still. Use a pilot or a conservative guess for
sd.
Both charge the full half-width twice (worst-case position of the centre);
typical stopping times are about half of the returned value, as measured in
the package's pilot study. Both are computable before any data are collected
and serve to size budget and n_max.
An integer number of instances, or Inf if not reached by max_t.
design <- comparison_design(margin = 0.02, bounds = c(0, 1)) planning_horizon(design, distance = 0.05) # guaranteed, Hoeffding planning_horizon(design, distance = 0.05, sd = 0.1) # variance-based approximationdesign <- comparison_design(margin = 0.02, bounds = c(0, 1)) planning_horizon(design, distance = 0.05) # guaranteed, Hoeffding planning_horizon(design, distance = 0.05, sd = 0.1) # variance-based approximation
Names of the available boundaries
seqbench_boundaries()seqbench_boundaries()
A character vector. "betting" is the default in
comparison_design(); "hoeffding" and "empirical_bernstein" are
conservative references; "bernstein_declared" requires a declared upper
bound on the standard deviation of the paired difference and is valid only
if that bound holds; "naive_fixed" is an invalid negative control for
experiments.
seqbench_boundaries()seqbench_boundaries()
Current decision of a comparison
stopping_decision(state)stopping_decision(state)
state |
A |
A character scalar: "A" (A relevantly better), "B",
"equivalent", "continue" or "inconclusive", with attribute
reason ("declaration", "budget", "n_max", "cs_empty" or NA).
state <- initialize_comparison(comparison_design(margin = 0.02, bounds = c(0, 1))) stopping_decision(state)state <- initialize_comparison(comparison_design(margin = 0.02, bounds = c(0, 1))) stopping_decision(state)
One row per instance with the estimate, the confidence sequence at that time, its running intersection, cumulative cost and the decision the procedure would have taken there.
## S3 method for class 'seqbench_comparison' tidy(x, ...)## S3 method for class 'seqbench_comparison' tidy(x, ...)
x |
A |
... |
Unused. |
A tibble with columns t, instance, n_seeds, x,
estimate, lower, upper, lower_running, upper_running, cost,
cost_cum, decision.
design <- comparison_design(margin = 0.05, bounds = c(0, 1), boundary = "hoeffding") state <- update_comparison(initialize_comparison(design), data.frame(instance = 1:5, loss_a = c(.1, .2, .1, .3, .2), loss_b = c(.5, .6, .4, .7, .5))) tidy(state)design <- comparison_design(margin = 0.05, bounds = c(0, 1), boundary = "hoeffding") state <- update_comparison(initialize_comparison(design), data.frame(instance = 1:5, loss_a = c(.1, .2, .1, .3, .2), loss_b = c(.5, .6, .4, .7, .5))) tidy(state)
Absorbs the losses of one or more new instances, in the order given, and advances the confidence sequence and the decision after each instance. The sequential unit is the instance: all rows of an instance (its seeds) are averaged into one observation. Updating stops at the first instance that triggers a terminal decision or exhausts the budget; rows after it are not consumed and a warning says so.
update_comparison(state, losses)update_comparison(state, losses)
state |
A |
losses |
A data frame with columns |
The updated seqbench_comparison.
design <- comparison_design(margin = 0.05, bounds = c(0, 1), boundary = "hoeffding") state <- initialize_comparison(design) set.seed(1) losses <- data.frame(instance = 1:50, loss_a = runif(50, 0, 0.5), loss_b = runif(50, 0.3, 0.8)) state <- update_comparison(state, losses) stopping_decision(state)design <- comparison_design(margin = 0.05, bounds = c(0, 1), boundary = "hoeffding") state <- initialize_comparison(design) set.seed(1) losses <- data.frame(instance = 1:50, loss_a = runif(50, 0, 0.5), loss_b = runif(50, 0.3, 0.8)) state <- update_comparison(state, losses) stopping_decision(state)