The until argument of each step verb accepts these predicates: functions
function(state, candidate) tested before each step, where candidate is
the state the pending step would produce (its trace row already appended).
If satisfied, the step is not taken and the verb returns state. So
em_step(1000, until = kl_flat(1e-12)) means "at most a thousand EM sweeps,
or until a sweep would decrease KL by less than 1e-12". Your own predicate
must accept both arguments, e.g.
function(state, candidate) nrow(candidate@trace) > 20.
Value
A function of (state, candidate), two ripr_states, returning
TRUE or FALSE.
Details
gap_below() and support_gap_below() estimate the Frank–Wolfe gap of
state over the whole null and over the current support respectively. They
ignore candidate, so can be asked of one state as gap_below(tol)(state).
kl_flat() is a per-step difference and bounds nothing: under slow linear
convergence a small dKL means converged or crawling. Use it as a budget.
Functions
kl_flat(): Checks whether the pending step would change the KL by less thantol, comparing the last trace row ofcandidatewith that ofstate. It is recommended forem_step()in particular.gap_below(): Checks whether the Frank–Wolfe gap ofstateis belowtol, reading the oracle cached instate@oracle. Insidefw_step()the cache is always there; elsewhere setrecord_gap = TRUEon the previous verb.support_gap_below(): Checks whether the Frank–Wolfe gap on the current support,max_c G(theta_c) - 1, is belowtol: whether the weights are optimal for the current atoms, not whether the fit is optimal.
Examples
set.seed(1)
fam <- multinomial_family(n_trials = 4L, k = 3L)
plurality <- null_model(
fam,
list(
simplex_region(vertices = rbind(c(0.5, 0.5, 0), c(0, 1, 0), c(0, 0, 1))),
simplex_region(vertices = rbind(c(0.5, 0, 0.5), c(0, 1, 0), c(0, 0, 1)))
)
)
Q <- fam(c(0.4, 0.35, 0.25))
state <- ripr_init(Q, plurality) |>
fw_step(times = 25L, until = gap_below(1e-2))
gap_below(1e-2)(state)
#> [1] TRUE
# Would one more EM sweep leave the KL flat?
kl_flat(1e-6)(state, em_step(state))
#> [1] FALSE
# A predicate of your own sees both states: here, stop before the support
# would grow past a dozen atoms.
too_many_atoms <- function(state, candidate) {
n_atoms(candidate@mixing) > 12L
}
fw_step(state, 10L, until = too_many_atoms)
#> <ripr_state>
#> null multinomial_family in 2 parts
#> iterate 12 atoms, KL 0.00580133
#> gap 0.00571
#> steps fw 11, lb 0, em 0, weight 0, sfw 0, ct 0 (1.3s)
#> trace 12 rows, 0 snapshots