Trait Sampler
pub trait Sampler: Send + Sync {
// Required methods
fn infer_relative_space(&self, study: &StudyView) -> Result<SpaceSchema>;
fn sample_relative(
&self,
study: &StudyView,
trial: &TrialMeta,
space: &SpaceSchema,
) -> Result<Assignment>;
fn sample_independent(
&self,
study: &StudyView,
trial: &TrialMeta,
name: &str,
dist: &Distribution,
) -> Result<ParamValue>;
fn reseed(&self, seed: u64);
// Provided methods
fn after_trial(&self, study: &StudyView, trial: &FrozenTrial) -> Result<()> { ... }
fn state(&self) -> Result<Option<SamplerState>> { ... }
fn snapshots_space(&self) -> bool { ... }
fn restore_state(&self, blob: &SamplerState) -> Result<()> { ... }
}Expand description
Chooses parameter values for a trial.
Object-safe and Send + Sync: a study holds Arc<dyn Sampler> and calls
it from every worker thread. Implementations must therefore keep any
interior mutability behind a lock — and prefer, where possible, to be
stateless and derive everything from
TrialMeta::sampler_seed.
§Determinism
A stateless sampler must produce, for a given (study seed, trial number, space), the same assignment on every machine, in every thread, in
every completion order. History-dependent samplers (TPE and later) cannot
promise that under parallelism; they promise single-worker determinism plus
replay, and must say so in their own documentation.
Required Methods§
fn infer_relative_space(&self, study: &StudyView) -> Result<SpaceSchema>
fn infer_relative_space(&self, study: &StudyView) -> Result<SpaceSchema>
Determines the joint space to sample in one shot for this trial.
When the study declares a static SpaceSchema, the answer is
normally that schema. Define-by-run studies infer it from history
instead — which is why this can fail.
Returning an empty schema is legal and means “sample everything independently”.
§Errors
Error::Sampler if the space cannot be
inferred.
fn sample_relative(
&self,
study: &StudyView,
trial: &TrialMeta,
space: &SpaceSchema,
) -> Result<Assignment>
fn sample_relative( &self, study: &StudyView, trial: &TrialMeta, space: &SpaceSchema, ) -> Result<Assignment>
Samples the joint space for a trial.
The returned assignment must bind every parameter in space and
nothing else; each value must lie in its declared support.
§Errors
Error::Sampler on failure;
Error::SpaceExhausted when an
exhaustive sampler has handed out every configuration.
fn sample_independent(
&self,
study: &StudyView,
trial: &TrialMeta,
name: &str,
dist: &Distribution,
) -> Result<ParamValue>
fn sample_independent( &self, study: &StudyView, trial: &TrialMeta, name: &str, dist: &Distribution, ) -> Result<ParamValue>
Samples one parameter that the relative space did not cover.
This is the fallback for define-by-run code that suggests a parameter nobody has seen before.
§Errors
Error::Sampler on failure.
Provided Methods§
fn after_trial(&self, study: &StudyView, trial: &FrozenTrial) -> Result<()>
fn after_trial(&self, study: &StudyView, trial: &FrozenTrial) -> Result<()>
Called once a trial reaches a terminal state.
History-dependent samplers update their model here. The default does nothing, which is correct for every stateless sampler.
§Errors
Error::Sampler on failure. A failure
here does not fail the trial.
fn state(&self) -> Result<Option<SamplerState>>
fn state(&self) -> Result<Option<SamplerState>>
The sampler’s persistable state, if it has any.
The framework writes it to Scope::Sampler so
that another process can pick the study up. Stateless samplers return
None, which is the default.
§Errors
Error::Sampler if the state cannot be
serialized.
fn snapshots_space(&self) -> bool
fn snapshots_space(&self) -> bool
Whether this sampler enumerates a snapshot of the space it was constructed with, rather than the space each ask presents.
This is open-search-spaces §12.2’s clause 1, stated by the sampler
itself because it is not observable from the outside: a snapshotting
sampler has state() == None yet still cannot honour a growing
bound — the snapshot’s cardinality would change underneath the
enumeration. A study that declares an open search space refuses such
a sampler at create. The default is false, which is correct for
every sampler that asks the space it is handed.
fn restore_state(&self, blob: &SamplerState) -> Result<()>
fn restore_state(&self, blob: &SamplerState) -> Result<()>
Restores a sampler from a previously persisted blob.
The framework calls this once, when a study is constructed or
resumed, before any sampling: it reads
Scope::Sampler and, if a blob is present,
hands it here so a stateful sampler continues from exactly where the last
handle left off. A stateful sampler holds its state behind interior
mutability (as Patient and the Wilcoxon
machinery already do), so &self is enough to overwrite it.
The default is a no-op, which is correct for every stateless sampler
(Random, Grid, Qmc, Tpe): they recompute their decision
from the trial number and the StudyView, persist nothing
(state is None, so this is never called with a blob
of theirs), and would be broken by a restore. A stateful sampler
(CMA-ES, whose evolution path cannot be recomputed from history)
overrides both methods together, and the pair must round-trip exactly
— a study torn down and resumed must reproduce, bit for bit, what a
never-closed study would have sampled next.
§Errors
Error::Sampler if the blob cannot be
decoded — for instance a version this sampler no longer understands.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".