Iterative-Deepening PCT.
PCT's bug-finding probability is 1 / (n · k^(d-1)) per iteration,
where d is the bug depth. Most bugs are shallow (d ≤ 3). Fixing
a single depth wastes compute either way:
- Too shallow (
d=2): misses bugs that need 3+ priority swaps. - Too deep (
d=6): each schedule is more "scrambled," so shallow bugs that PCT-3 would hit on iteration 5 might take iteration 50 under PCT-6.
IDPCT cycles depth across iterations: iteration 1 uses depth d_min,
iteration 2 uses d_min+1, ..., wrapping at d_max. A run of N
iterations covers each depth in [d_min, d_max] roughly N/(d_max-d_min+1)
times, dominating PCT for the case where bug depth is unknown.
Options
:seed— RNG seed (required, used by underlying PCT):max_steps— total scheduling steps (required):d_min— minimum bug depth, default 2:d_max— maximum bug depth, default 6:high_prio_max— same as PCT, default 1_000_000
All other options are forwarded to Lockstep.Strategy.PCT after
stripping :d_min / :d_max and computing :bug_depth.
When to use
Reach for IDPCT when:
- You don't know the bug depth a priori (most cases).
- You're running many iterations (≥ 1000) and want to amortize cost across depths.
- You want the fastest finds for shallow bugs while still retaining a chance at deep ones.
Stick with plain PCT (:pct) when:
- You're doing replay or focused exploration and want a single, predictable depth.
- Iteration count is low (< 100) and you want maximum probability per iteration at a known-good depth.
Implementation
IDPCT is a thin wrapper that derives bug_depth from seed (so
iterations 1, 2, 3, ... cycle deterministically through the depth
range), then delegates init/1, on_register/2, and pick/2 to
Lockstep.Strategy.PCT. The strategy state is literally a PCT
state — no new struct required.