Crate atune_plugin_example
Expand description
An out-of-tree sampler, written the way a plugin author would write one.
§Why this crate exists
Milestone M7’s exit criterion asks that “the guide’s example plugin crate
compiles against the frozen traits”. Nothing in the repository provisioned such
a crate: examples/rust/custom_sampler.rs is a workspace example binary,
compiled as part of atune with the facade and all its features in scope,
which is not the situation a plugin author is in. M7.D7 tested the criterion by
writing a crate outside the repository — it compiled, its property test passed,
and then it was gone, leaving nothing that keeps the criterion met. This crate
is that check, made standing: the workspace gate compiles it on every run.
It is deliberately not a copy of custom_sampler.rs. A second copy of the
same sampler would prove only that the same code still compiles; a different
one — antithetic pairs rather than stratification — exercises a different part
of the seam and reads as an independent implementation, which is what the
criterion is really about.
§What it proves, and what it does not
cargo test -p atune_plugin_example proves the strong statement: the seams are
implementable with atune_core as the only dependency and its default
features off, because -p is what keeps cargo from unifying system back
in from the other workspace members. A workspace-wide cargo test compiles
this crate too, but with system unified on, so it proves the weaker “the
traits are usable from outside” — still worth having, and it is the run that
happens on every push.
The dispatch-only release-staging workflow adds a stronger package-boundary
check. It copies this crate unchanged into a disposable workspace, places the
extracted atune_core crate archive at this manifest’s relative path, proves
that no atune_core feature was enabled, then builds and runs these tests.
That is proof about the staged archive bytes, not a claim that a registry
publication exists; only a clean consumer resolving the published version can
close that final gap.
§The sampler
Antithetic is uniform random search with its draws mirrored in pairs. Trial
2k draws a unit coordinate u for each parameter; trial 2k + 1 draws
1 - u for the same parameter. The pair straddles the middle of the support by
construction, so a run of 2n trials cannot spend all of its budget on one
side of a range the way 2n independent uniform draws sometimes do — the
classic variance-reduction trick, and one that fits the seam exactly because
the framework hands a sampler the trial number and a seed derived from it.
One property comes out of the trait’s own contract rather than from this
implementation being careful: per-name derivation, so a value survives an
edit to the search space. The seed for a parameter is
seed_for_name(trial seed, name), never the next number out of one
per-trial stream, which is what makes x keep its value when y is added
beside it.
§What writing it found out, and what it costs
An antithetic sampler needs trial 2k + 1 to know what trial 2k drew, and the
seam gives it no way to derive that: TrialMeta::sampler_seed is already
per-trial-number, so the two halves of a pair are handed unrelated seeds, and
StudyView exposes directions, the space and trial iterators but no study
seed. Sampler::reseed, the natural place to receive one, is never called
outside tests. The only working route is the one below: read the predecessor’s
recorded value back out of the view and mirror that.
That is a real cost, stated here because a plugin author pays it too. It moves
this sampler off the stateless row of the seam’s own table — it is
history-dependent, like Tpe, so it promises single-worker determinism plus
replay rather than order-independence, and a two-worker run may pair trials
differently from a one-worker run. parallelism(1) is therefore part of the
contract of the test below, not an accident of it. Nothing is persisted
either way: Sampler::state stays None, because everything it needs is
already in the study’s own history.
Structs§
- Antithetic
- Uniform sampling with antithetic pairs: trial
2k + 1mirrors trial2k.