Skip to main content

Sampler

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>

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>

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>

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.

fn reseed(&self, seed: u64)

Reseeds the sampler.

Needed for the parallel-worker case, where two workers must not draw the same auxiliary randomness. Per-trial sampling seeds arrive through TrialMeta instead and are unaffected by this call.

Provided Methods§

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>>

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

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

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<()>

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".

Implementors§

§

impl Sampler for Dehb

§

impl Sampler for Grid

§

impl Sampler for Nsga2

§

impl Sampler for Nsga3

§

impl Sampler for Qmc

§

impl Sampler for Random

§

impl Sampler for Tpe