Skip to main content

Module sampler

Module sampler 

Source
Expand description

Sampler trait and implementations for parameter sampling.

A sampler generates parameter values for each trial. It receives a Distribution describing the parameter space, a monotonically increasing trial_id, and the list of all CompletedTrials so far, and returns a ParamValue that matches the distribution variant.

§Available samplers

§Single-objective

SamplerAlgorithmBest for
RandomSamplerUniform independent samplingBaselines, startup phases
TpeSamplerTree-Parzen EstimatorGeneral-purpose Bayesian optimization
TpeSampler (multivariate)Multivariate TPE with tree-structured ParzenCorrelated parameters
GridSamplerExhaustive grid evaluationSmall discrete spaces
[SobolSampler]*Quasi-random Sobol sequencesUniform coverage without model
[CmaEsSampler]*Covariance Matrix AdaptationContinuous, non-separable problems
[GpSampler]*Gaussian Process with EIExpensive, low-dimensional functions
DESamplerDifferential EvolutionPopulation-based, multi-modal landscapes
BohbSamplerBayesian Optimization + HyperBandCombined sampling and pruning

*Requires a feature flag (sobol, cma-es, or gp).

§Multi-objective

SamplerAlgorithmBest for
Nsga2SamplerNSGA-IIGeneral multi-objective with 2-3 objectives
Nsga3SamplerNSGA-IIIMany-objective (4+ objectives)
MoeadSamplerMOEA/D with decompositionStructured Pareto front exploration
MotpeSamplerMulti-objective TPEBayesian multi-objective

§Implementing a custom sampler

Implement the Sampler trait with its single method:

use optimizer::sampler::{Sampler, CompletedTrial};
use optimizer::distribution::Distribution;
use optimizer::param::ParamValue;

/// A sampler that always picks the midpoint of each distribution.
struct MidpointSampler;

impl Sampler for MidpointSampler {
    fn sample(
        &self,
        distribution: &Distribution,
        _trial_id: u64,
        _history: &[CompletedTrial],
    ) -> ParamValue {
        match distribution {
            Distribution::Float(fd) => {
                ParamValue::Float((fd.low + fd.high) / 2.0)
            }
            Distribution::Int(id) => {
                ParamValue::Int((id.low + id.high) / 2)
            }
            Distribution::Categorical(cd) => {
                ParamValue::Categorical(cd.n_choices / 2)
            }
        }
    }
}

The arguments to Sampler::sample:

  • distribution — a Distribution::Float, Distribution::Int, or Distribution::Categorical that describes the parameter bounds, log-scale flag, and optional step size.
  • trial_id — a monotonically increasing identifier. Useful for deterministic RNG seeding (see Stateless vs stateful samplers).
  • history — all completed trials so far. May be empty on the first trial. Model-based samplers use this to guide future sampling.
  • Return value — the ParamValue variant must match the distribution variant (FloatParamValue::Float, etc.).

§Stateless vs stateful samplers

Stateless samplers derive all randomness from a deterministic function of seed + trial_id + distribution. They use an AtomicU64 call-sequence counter to disambiguate multiple calls within the same trial, but need no Mutex. See RandomSampler and TpeSampler for this pattern.

Stateful samplers maintain mutable state (e.g. a population pool) across calls. Wrap mutable state in parking_lot::Mutex<State> and lock for the duration of Sampler::sample. See DESampler and GridSampler for this pattern.

§Cold start handling

Model-based samplers need completed trials before their surrogate model is useful. The standard pattern is to check history.len() < n_startup_trials and fall back to random sampling during the startup phase. Expose this as a builder parameter so users can tune the trade-off between exploration and exploitation. See TpeSampler for a reference implementation.

§Reading trial history

The history slice contains only completed trials (never pending ones). Common operations:

  • Extract a parameter value: trial.params.get(&param_id) returns Option<&ParamValue>.
  • Find the best trial: history.iter().min_by(|a, b| a.value.partial_cmp(&b.value).unwrap()).
  • Filter by state: history.iter().filter(|t| t.state == TrialState::Complete).
  • Check feasibility: trial.is_feasible() returns true when all constraints are ≤ 0.

§Thread safety

The Sampler trait requires Send + Sync. Study stores the sampler as Arc<dyn Sampler>, so multiple threads may call Sampler::sample concurrently.

  • Stateless: AtomicU64 counters satisfy Send + Sync without locking.
  • Stateful: use parking_lot::Mutex (the crate convention) or std::sync::Mutex to protect mutable state.

§Testing custom samplers

Recommended test categories:

  1. Bounds compliance — sample many values and assert they fall within the distribution range.
  2. Step / log-scale correctness — verify that discretized and log-scaled distributions produce valid values.
  3. Reproducibility — the same seed must produce the same output.
  4. History sensitivity — model-based samplers should produce different (better) samples as history grows.
  5. Empty historysample() must not panic when history is empty.

§Using a custom sampler with Study

use optimizer::{Direction, Study};
use optimizer::sampler::{Sampler, CompletedTrial};
use optimizer::distribution::Distribution;
use optimizer::param::ParamValue;

struct MySampler;
impl Sampler for MySampler {
    fn sample(
        &self,
        distribution: &Distribution,
        _trial_id: u64,
        _history: &[CompletedTrial],
    ) -> ParamValue {
        match distribution {
            Distribution::Float(fd) => ParamValue::Float(fd.low),
            Distribution::Int(id) => ParamValue::Int(id.low),
            Distribution::Categorical(_) => ParamValue::Categorical(0),
        }
    }
}

let study: Study<f64> = Study::with_sampler(Direction::Minimize, MySampler);

The sampler is wrapped in Arc<dyn Sampler> internally.

§Reference implementations

Re-exports§

pub use bohb::BohbSampler;
pub use de::DESampler;
pub use de::DEStrategy;
pub use grid::GridSampler;
pub use moead::Decomposition;
pub use moead::MoeadSampler;
pub use motpe::MotpeSampler;
pub use nsga2::Nsga2Sampler;
pub use nsga3::Nsga3Sampler;
pub use random::RandomSampler;
pub use tpe::TpeSampler;

Modules§

bohb
BOHB (Bayesian Optimization + HyperBand) sampler.
de
Differential Evolution (DE) sampler.
grid
Grid search sampler — exhaustive evaluation of discretized parameter spaces.
moead
MOEA/D (Multi-Objective Evolutionary Algorithm based on Decomposition) sampler.
motpe
Multi-Objective Tree-Parzen Estimator (MOTPE) sampler.
nsga2
NSGA-II (Non-dominated Sorting Genetic Algorithm II) sampler.
nsga3
NSGA-III (Non-dominated Sorting Genetic Algorithm III) sampler.
random
Random sampler — uniform independent sampling.
tpe
Tree-Parzen Estimator (TPE) sampler family for Bayesian optimization.

Structs§

CompletedTrial
A completed trial with its parameters, distributions, and objective value.
PendingTrial
A pending (running) trial with its parameters and distributions, but no objective value yet.

Traits§

Sampler
Trait for pluggable parameter sampling strategies.