Skip to contents

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.

Usage

kl_flat(tol = 1e-12)

gap_below(tol = 1e-08)

support_gap_below(tol = 1e-08)

Arguments

tol

Threshold.

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 than tol, comparing the last trace row of candidate with that of state. It is recommended for em_step() in particular.

  • gap_below(): Checks whether the Frank–Wolfe gap of state is below tol, reading the oracle cached in state@oracle. Inside fw_step() the cache is always there; elsewhere set record_gap = TRUE on the previous verb.

  • support_gap_below(): Checks whether the Frank–Wolfe gap on the current support, max_c G(theta_c) - 1, is below tol: 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