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 reads_constraints(&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.
This runs inside the study’s finalization. It must not block on a
Study::optimize of the same study
(for example by joining the thread running it): that loop’s ask is
refused with backpressure until this returns, and it retries rather
than fails, so neither ever finishes.
§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.
The sampler states this 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 reads_constraints(&self) -> bool
fn reads_constraints(&self) -> bool
Whether this sampler reads FrozenTrial::constraints on the views it
is asked with.
Constraint values live in a per-trial state blob, not on the trial
record, so a view synced from storage carries none. A sampler that
returns true has the study attach them to every completed and pruned
trial of its ask-time view, at the cost of one state read per newly
synced finished trial. The default is false: the view stays raw and
the sampler pays nothing. after_trial receives
the finishing trial’s constraints either way.
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 rejects a blob: stateless samplers persist nothing, so a blob here belongs to a different sampler or an incompatible prior implementation. Silently accepting it would make an algorithm switch look like a successful resume. A stateful sampler 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::Incompatible if the
default restore receives a blob; Error::Sampler
if an overriding implementation cannot decode it.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".