Skip to main content

hyperopt_samplers/
random.rs

1use hyperopt_core::{Distribution, Sampler, StudyState, Trial, Value};
2use rand::rngs::StdRng;
3use rand::{RngExt, SeedableRng};
4
5/// Draw a single value from a distribution with no adaptive state. Shared by
6/// [`RandomSampler`] and used by [`crate::TpeSampler`] during its startup
7/// (pre-model) phase.
8pub(crate) fn sample_value(rng: &mut StdRng, distribution: &Distribution) -> Value {
9    match distribution {
10        Distribution::Uniform { low, high } => {
11            if low < high {
12                Value::Float(rng.random_range(*low..*high))
13            } else {
14                Value::Float(*low)
15            }
16        }
17        Distribution::LogUniform { low, high } => {
18            if *low > 0.0 && low < high {
19                let l = low.ln();
20                let h = high.ln();
21                Value::Float(rng.random_range(l..h).exp())
22            } else {
23                Value::Float(*low)
24            }
25        }
26        Distribution::IntUniform { low, high } => {
27            if low <= high {
28                Value::Int(rng.random_range(*low..=*high))
29            } else {
30                Value::Int(*low)
31            }
32        }
33        Distribution::Categorical { choices } => {
34            if choices.is_empty() {
35                Value::Categorical(String::new())
36            } else {
37                let i = rng.random_range(0..choices.len());
38                Value::Categorical(choices[i].clone())
39            }
40        }
41    }
42}
43
44/// The simplest possible [`Sampler`]: every parameter is drawn independently
45/// and uniformly (or log-uniformly / categorically) from its distribution, with
46/// no learning from prior trials. It is the right baseline to validate the
47/// whole `Trial`/`TrialContext`/`Study` plumbing against, and the reference
48/// every adaptive sampler is compared to.
49pub struct RandomSampler {
50    rng: StdRng,
51}
52
53impl RandomSampler {
54    /// A random sampler seeded from OS entropy (non-deterministic across runs).
55    pub fn new() -> Self {
56        let mut seeder = rand::rng();
57        RandomSampler {
58            rng: StdRng::seed_from_u64(seeder.random()),
59        }
60    }
61
62    /// A random sampler with a fixed seed — reproducible across runs, which is
63    /// what tests and benchmarks want.
64    pub fn seeded(seed: u64) -> Self {
65        RandomSampler {
66            rng: StdRng::seed_from_u64(seed),
67        }
68    }
69}
70
71impl Default for RandomSampler {
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77impl Sampler for RandomSampler {
78    fn suggest(
79        &mut self,
80        _study_state: &StudyState,
81        _trial: &Trial,
82        _param_name: &str,
83        distribution: &Distribution,
84    ) -> Value {
85        sample_value(&mut self.rng, distribution)
86    }
87}