pub enum Acquisition {
Random,
Thompson,
Bald,
}Expand description
Which rule picks the next duel.
Selectable because the choice is an empirical claim, and
learn_synthetic --compare measures it. Both alternatives are kept so
that comparison stays runnable — a rule chosen on evidence should stay
re-checkable, and a rule rejected on evidence doubly so.
§The measurement, and what it is a measurement of
cargo run -p auracle-session --example learn_synthetic --release -- --compare 20, on the synthetic user: 20 seeds, 72 duels, refit every 12.
Common random numbers — pool fill, the user’s coin flip at duel t,
MCMC seed at round r, and refinement seeds are all shared across arms,
so only the acquisition draw differs. Both regimes are graded on one fixed
held-out exam under a single reference scale, so arms that built different
pools are still answering the same questions. ± is two standard errors
of the paired difference.
§Static pool (i.i.d. prior draws, refine_steps: 0)
| cos θ* ↑ | rank r ↑ | excess nats ↓ | |
|---|---|---|---|
| random | 0.460 | 0.731 | 0.211 |
| thompson | 0.416 | 0.628 | 0.254 |
| bald | 0.484 | 0.762 | 0.199 |
| bald − thompson | +0.068 ± 0.062 | +0.134 ± 0.044 | −0.055 ± 0.014 |
| bald − random | +0.025 ± 0.058 | +0.031 ± 0.046 | −0.012 ± 0.013 |
Dueling Thompson sampling is the one clear loser, at t = 2.2 / 6.1 / −8.0. It is a best-arm rule: it converges on identifying the top patch, which is not what a duel is for here. BALD and uniform pairing are inside two standard errors of each other on every metric.
A static i.i.d. pool is also a weak regime to conclude from on its own:
prior draws are spread over feature space by construction, which is
exactly where uniform pairs already achieve near-optimal ‖φ_a − φ_b‖
coverage and an information-seeking rule has no redundancy to prune. The
concern was that the shipped pool is not that pool — refinement injects
children near the current best and insert_candidate evicts the worst —
so --compare runs an evolving regime too, with real refinement
between rounds (the Regime type in learn_synthetic.rs documents the
design).
§Evolving pool (refine_steps: 12, refinement between rounds)
| cos θ* ↑ | rank r ↑ | excess nats ↓ | |
|---|---|---|---|
| random | 0.479 | 0.694 | 0.232 |
| thompson | 0.459 | 0.583 | 0.276 |
| bald | 0.465 | 0.707 | 0.232 |
| bald − thompson | +0.006 ± 0.068 | +0.124 ± 0.066 | −0.044 ± 0.017 |
| bald − random | −0.015 ± 0.055 | +0.013 ± 0.048 | −0.000 ± 0.014 |
Same answer: Thompson loses, BALD and uniform pairing tie on every metric.
The run’s manipulation check is itself a finding. Final pool spread (mean
pairwise ‖Δφ‖, reference scale) was 7.7–7.9 evolving vs 7.2 static:
six generations over a 72-duel session did not concentrate the pool at
all — frontier-biased injection plus worst-eviction widened it slightly,
because mutation pushes children into feature-space extremes faster than
eviction trims them. So the concentrated regime BALD was hypothesized to
win never arises at session horizon, and the tie is not an artifact of a
spread pool that only the static setup guaranteed — the product’s own
dynamics keep the pool spread.
§Why Random is the default
Measured in both the regime the product starts in and the regime it
evolves into, uniform pairing is indistinguishable from BALD — and a rule
with four tuning constants that ties a rule with none should not ship on
a tie. Two supporting justifications survived checking, one did not: the
info_gain BALD reports had zero consumers in the frontend, and BALD’s
repeat avoidance, while real, is barely needed over a 48-candidate pool
that uniform pairing already samples without repeating (measured in
duels_spread_over_candidates_not_just_pairs). Random also makes
every duel an unbiased calibration sample rather than one in ten —
a virtue that holds regardless of which rule learns θ faster.
One earlier justification was retracted for a bad reason, and the record
should say so. The “pool grows and concentrates” argument was dismissed on
the grounds that insert_candidate caps the pool — but a capped size is
not an unchanging spread, and evicting the worst member could in
principle concentrate a pool. Dismissing the concentration argument
because it was unmeasured, while treating a measurement from the other
regime as decisive, had the burden of proof backwards. The evolving run
above is that measurement; it happens to show the concentration never
materializes, but the default rests on the measured tie, not on the
dismissal.
§What Bald is still for
It is not dead code and it is not a fallback. It decisively beats the
best-arm rule, so it is the right thing to reach for if acquisition ever
needs to do something uniform pairing cannot: bias duels toward patches
the user will enjoy auditioning (SessionConfig::duel_utility_weight),
bound how often one patch reappears (SessionConfig::duel_exposure_penalty),
or report why a question was asked. Those levers exist and are measured;
none of them is currently worth the tie.
§A correction worth recording
An earlier version of this rule scored its enjoyment term on unnormalized
utility and used an absolute softmax temperature of 0.05 nats. Both are
scale bets, and both lost: the enjoyment term grew without bound as the
posterior sharpened, and exp(ΔJ/T) ran to e¹⁰, so the “softmax” was an
argmax. That version was measurably worse than random, and it is the
version an independent replication measured. It is also what produced the
duel repetition seen in the running app — the same defect, observed from
two directions. Fixed, BALD ties random; the numbers above are the fixed
rule.
Variants§
Random
Uniformly random pairs. The default — see the type doc.
Thompson
Dueling Thompson sampling: two posterior draws, duel their champions. Best-arm identification — converges on the top patch, not on θ.
Bald
Expected information gain about θ, plus an enjoyment term and a
repeat penalty, sampled from a softmax. Beats Acquisition::Thompson
decisively and ties Acquisition::Random; see the type doc.
Trait Implementations§
Source§impl Clone for Acquisition
impl Clone for Acquisition
Source§fn clone(&self) -> Acquisition
fn clone(&self) -> Acquisition
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for Acquisition
impl Debug for Acquisition
Source§impl Default for Acquisition
impl Default for Acquisition
Source§fn default() -> Acquisition
fn default() -> Acquisition
Source§impl PartialEq for Acquisition
impl PartialEq for Acquisition
Source§fn eq(&self, other: &Acquisition) -> bool
fn eq(&self, other: &Acquisition) -> bool
self and other values to be equal, and is used by ==.