refactor: shorten sampler type names

- GridSearchSampler → GridSampler
- DifferentialEvolutionSampler → DESampler
- DifferentialEvolutionStrategy → DEStrategy
- DifferentialEvolutionSamplerBuilder → DESamplerBuilder
This commit is contained in:
Manuel Raimann
2026-02-12 13:19:06 +01:00
parent 47b5f9cec8
commit d20f09c66a
7 changed files with 109 additions and 137 deletions
+2 -2
View File
@@ -2,7 +2,7 @@ use std::collections::HashMap;
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use optimizer::parameter::{FloatParam, Parameter}; use optimizer::parameter::{FloatParam, Parameter};
use optimizer::sampler::grid::GridSearchSampler; use optimizer::sampler::grid::GridSampler;
use optimizer::sampler::random::RandomSampler; use optimizer::sampler::random::RandomSampler;
use optimizer::sampler::tpe::TpeSampler; use optimizer::sampler::tpe::TpeSampler;
use optimizer::sampler::{CompletedTrial, Sampler}; use optimizer::sampler::{CompletedTrial, Sampler};
@@ -97,7 +97,7 @@ fn bench_grid_sample(c: &mut Criterion) {
|b, _| { |b, _| {
b.iter(|| { b.iter(|| {
// Fresh sampler each iteration since grid tracks used points // Fresh sampler each iteration since grid tracks used points
let sampler = GridSearchSampler::builder() let sampler = GridSampler::builder()
.n_points_per_param(grid_points) .n_points_per_param(grid_points)
.build(); .build();
sampler.sample(&dist, 0, &history) sampler.sample(&dist, 0, &history)
+1 -1
View File
@@ -65,7 +65,7 @@ fn main() {
// Evaluates evenly spaced grid points. Each parameter gets its own grid that // Evaluates evenly spaced grid points. Each parameter gets its own grid that
// is sampled in order, so n_points_per_param must be >= n_trials. // is sampled in order, so n_points_per_param must be >= n_trials.
println!("\n3. Grid sampler (exhaustive):"); println!("\n3. Grid sampler (exhaustive):");
let grid = GridSearchSampler::builder() let grid = GridSampler::builder()
.n_points_per_param(n_trials) // one grid point per trial per parameter .n_points_per_param(n_trials) // one grid point per trial per parameter
.build(); .build();
let grid_best = run_study(Study::minimize(grid), n_trials); let grid_best = run_study(Study::minimize(grid), n_trials);
+4 -5
View File
@@ -54,11 +54,11 @@
//! |---------|-----------|----------|--------------| //! |---------|-----------|----------|--------------|
//! | [`RandomSampler`](sampler::RandomSampler) | Uniform random | Baselines, high-dimensional | — | //! | [`RandomSampler`](sampler::RandomSampler) | Uniform random | Baselines, high-dimensional | — |
//! | [`TpeSampler`](sampler::TpeSampler) | Tree-Parzen Estimator | General-purpose Bayesian | — | //! | [`TpeSampler`](sampler::TpeSampler) | Tree-Parzen Estimator | General-purpose Bayesian | — |
//! | [`GridSearchSampler`](sampler::GridSearchSampler) | Exhaustive grid | Small, discrete spaces | — | //! | [`GridSearchSampler`](sampler::GridSampler) | Exhaustive grid | Small, discrete spaces | — |
//! | [`SobolSampler`](sampler::SobolSampler) | Sobol quasi-random sequence | Space-filling, low dimensions | `sobol` | //! | [`SobolSampler`](sampler::SobolSampler) | Sobol quasi-random sequence | Space-filling, low dimensions | `sobol` |
//! | [`CmaEsSampler`](sampler::CmaEsSampler) | CMA-ES | Continuous, moderate dimensions | `cma-es` | //! | [`CmaEsSampler`](sampler::CmaEsSampler) | CMA-ES | Continuous, moderate dimensions | `cma-es` |
//! | [`GpSampler`](sampler::GpSampler) | Gaussian Process + EI | Expensive objectives, few trials | `gp` | //! | [`GpSampler`](sampler::GpSampler) | Gaussian Process + EI | Expensive objectives, few trials | `gp` |
//! | [`DifferentialEvolutionSampler`](sampler::DifferentialEvolutionSampler) | Differential Evolution | Non-convex, population-based | — | //! | [`DESampler`](sampler::DESampler) | Differential Evolution | Non-convex, population-based | — |
//! | [`BohbSampler`](sampler::BohbSampler) | BOHB (TPE + `HyperBand`) | Budget-aware early stopping | — | //! | [`BohbSampler`](sampler::BohbSampler) | BOHB (TPE + `HyperBand`) | Budget-aware early stopping | — |
//! //!
//! ## Multi-objective samplers //! ## Multi-objective samplers
@@ -168,9 +168,8 @@ pub mod prelude {
#[cfg(feature = "sobol")] #[cfg(feature = "sobol")]
pub use crate::sampler::SobolSampler; pub use crate::sampler::SobolSampler;
pub use crate::sampler::{ pub use crate::sampler::{
BohbSampler, CompletedTrial, Decomposition, DifferentialEvolutionSampler, BohbSampler, CompletedTrial, DESampler, DEStrategy, Decomposition, GridSampler,
DifferentialEvolutionStrategy, GridSearchSampler, MoeadSampler, MotpeSampler, Nsga2Sampler, MoeadSampler, MotpeSampler, Nsga2Sampler, Nsga3Sampler, RandomSampler, TpeSampler,
Nsga3Sampler, RandomSampler, TpeSampler,
}; };
#[cfg(feature = "journal")] #[cfg(feature = "journal")]
pub use crate::storage::JournalStorage; pub use crate::storage::JournalStorage;
+45 -69
View File
@@ -10,7 +10,7 @@
//! //!
//! Each generation, for every population member *xᵢ*: //! Each generation, for every population member *xᵢ*:
//! 1. **Mutation** — create a mutant vector *v* from other population //! 1. **Mutation** — create a mutant vector *v* from other population
//! members using the selected [`DifferentialEvolutionStrategy`]: //! members using the selected [`DEStrategy`]:
//! - `Rand1`: `v = x_r1 + F * (x_r2 - x_r3)` //! - `Rand1`: `v = x_r1 + F * (x_r2 - x_r3)`
//! - `Best1`: `v = x_best + F * (x_r1 - x_r2)` //! - `Best1`: `v = x_best + F * (x_r1 - x_r2)`
//! - `CurrentToBest1`: `v = x_i + F * (x_best - x_i) + F * (x_r1 - x_r2)` //! - `CurrentToBest1`: `v = x_i + F * (x_best - x_i) + F * (x_r1 - x_r2)`
@@ -40,20 +40,20 @@
//! | `population_size` | `max(10n, 15)` | Candidates per generation | //! | `population_size` | `max(10n, 15)` | Candidates per generation |
//! | `mutation_factor` (F) | 0.8 | Differential amplification — higher = more exploration | //! | `mutation_factor` (F) | 0.8 | Differential amplification — higher = more exploration |
//! | `crossover_rate` (CR) | 0.9 | Probability of taking a dimension from the mutant | //! | `crossover_rate` (CR) | 0.9 | Probability of taking a dimension from the mutant |
//! | `strategy` | `Rand1` | Mutation strategy (see [`DifferentialEvolutionStrategy`]) | //! | `strategy` | `Rand1` | Mutation strategy (see [`DEStrategy`]) |
//! | `seed` | random | RNG seed for reproducibility | //! | `seed` | random | RNG seed for reproducibility |
//! //!
//! # Examples //! # Examples
//! //!
//! ``` //! ```
//! use optimizer::sampler::de::{DifferentialEvolutionSampler, DifferentialEvolutionStrategy}; //! use optimizer::sampler::de::{DESampler, DEStrategy};
//! use optimizer::{Direction, Study}; //! use optimizer::{Direction, Study};
//! //!
//! // Minimize with DE using the Best1 strategy for faster convergence //! // Minimize with DE using the Best1 strategy for faster convergence
//! let sampler = DifferentialEvolutionSampler::builder() //! let sampler = DESampler::builder()
//! .mutation_factor(0.7) //! .mutation_factor(0.7)
//! .crossover_rate(0.9) //! .crossover_rate(0.9)
//! .strategy(DifferentialEvolutionStrategy::Best1) //! .strategy(DEStrategy::Best1)
//! .population_size(20) //! .population_size(20)
//! .seed(42) //! .seed(42)
//! .build(); //! .build();
@@ -74,7 +74,7 @@ use crate::sampler::{CompletedTrial, Sampler};
/// ///
/// Controls how mutant vectors are created from the current population. /// Controls how mutant vectors are created from the current population.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub enum DifferentialEvolutionStrategy { pub enum DEStrategy {
/// DE/rand/1: `v = x_r1 + F * (x_r2 - x_r3)` /// DE/rand/1: `v = x_r1 + F * (x_r2 - x_r3)`
/// ///
/// The most robust strategy. Uses three random population members. /// The most robust strategy. Uses three random population members.
@@ -99,46 +99,36 @@ pub enum DifferentialEvolutionStrategy {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use optimizer::sampler::de::DifferentialEvolutionSampler; /// use optimizer::sampler::de::DESampler;
/// use optimizer::{Direction, Study}; /// use optimizer::{Direction, Study};
/// ///
/// // Default configuration /// // Default configuration
/// let study: Study<f64> = /// let study: Study<f64> = Study::with_sampler(Direction::Minimize, DESampler::new());
/// Study::with_sampler(Direction::Minimize, DifferentialEvolutionSampler::new());
/// ///
/// // With seed for reproducibility /// // With seed for reproducibility
/// let study: Study<f64> = Study::with_sampler( /// let study: Study<f64> = Study::with_sampler(Direction::Minimize, DESampler::with_seed(42));
/// Direction::Minimize,
/// DifferentialEvolutionSampler::with_seed(42),
/// );
/// ///
/// // Custom configuration via builder /// // Custom configuration via builder
/// use optimizer::sampler::de::DifferentialEvolutionStrategy; /// use optimizer::sampler::de::DEStrategy;
/// let sampler = DifferentialEvolutionSampler::builder() /// let sampler = DESampler::builder()
/// .mutation_factor(0.8) /// .mutation_factor(0.8)
/// .crossover_rate(0.9) /// .crossover_rate(0.9)
/// .strategy(DifferentialEvolutionStrategy::Best1) /// .strategy(DEStrategy::Best1)
/// .population_size(30) /// .population_size(30)
/// .seed(42) /// .seed(42)
/// .build(); /// .build();
/// let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); /// let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
/// ``` /// ```
pub struct DifferentialEvolutionSampler { pub struct DESampler {
state: Mutex<State>, state: Mutex<State>,
} }
impl DifferentialEvolutionSampler { impl DESampler {
/// Creates a new DE sampler with default settings and a random seed. /// Creates a new DE sampler with default settings and a random seed.
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
state: Mutex::new(State::new( state: Mutex::new(State::new(None, 0.8, 0.9, DEStrategy::Rand1, None)),
None,
0.8,
0.9,
DifferentialEvolutionStrategy::Rand1,
None,
)),
} }
} }
@@ -146,30 +136,24 @@ impl DifferentialEvolutionSampler {
#[must_use] #[must_use]
pub fn with_seed(seed: u64) -> Self { pub fn with_seed(seed: u64) -> Self {
Self { Self {
state: Mutex::new(State::new( state: Mutex::new(State::new(None, 0.8, 0.9, DEStrategy::Rand1, Some(seed))),
None,
0.8,
0.9,
DifferentialEvolutionStrategy::Rand1,
Some(seed),
)),
} }
} }
/// Creates a builder for configuring a `DifferentialEvolutionSampler`. /// Creates a builder for configuring a `DESampler`.
#[must_use] #[must_use]
pub fn builder() -> DifferentialEvolutionSamplerBuilder { pub fn builder() -> DESamplerBuilder {
DifferentialEvolutionSamplerBuilder::new() DESamplerBuilder::new()
} }
} }
impl Default for DifferentialEvolutionSampler { impl Default for DESampler {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
/// Builder for configuring a [`DifferentialEvolutionSampler`]. /// Builder for configuring a [`DESampler`].
/// ///
/// All options have sensible defaults: /// All options have sensible defaults:
/// - `population_size`: `max(10 * n_dims, 15)` (auto-computed from parameter count) /// - `population_size`: `max(10 * n_dims, 15)` (auto-computed from parameter count)
@@ -181,34 +165,32 @@ impl Default for DifferentialEvolutionSampler {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use optimizer::sampler::de::{ /// use optimizer::sampler::de::{DESamplerBuilder, DEStrategy};
/// DifferentialEvolutionSamplerBuilder, DifferentialEvolutionStrategy,
/// };
/// ///
/// let sampler = DifferentialEvolutionSamplerBuilder::new() /// let sampler = DESamplerBuilder::new()
/// .mutation_factor(0.5) /// .mutation_factor(0.5)
/// .crossover_rate(0.7) /// .crossover_rate(0.7)
/// .strategy(DifferentialEvolutionStrategy::CurrentToBest1) /// .strategy(DEStrategy::CurrentToBest1)
/// .population_size(20) /// .population_size(20)
/// .seed(42) /// .seed(42)
/// .build(); /// .build();
/// ``` /// ```
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DifferentialEvolutionSamplerBuilder { pub struct DESamplerBuilder {
population_size: Option<usize>, population_size: Option<usize>,
mutation_factor: f64, mutation_factor: f64,
crossover_rate: f64, crossover_rate: f64,
strategy: DifferentialEvolutionStrategy, strategy: DEStrategy,
seed: Option<u64>, seed: Option<u64>,
} }
impl Default for DifferentialEvolutionSamplerBuilder { impl Default for DESamplerBuilder {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl DifferentialEvolutionSamplerBuilder { impl DESamplerBuilder {
/// Creates a new builder with default settings. /// Creates a new builder with default settings.
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
@@ -216,7 +198,7 @@ impl DifferentialEvolutionSamplerBuilder {
population_size: None, population_size: None,
mutation_factor: 0.8, mutation_factor: 0.8,
crossover_rate: 0.9, crossover_rate: 0.9,
strategy: DifferentialEvolutionStrategy::Rand1, strategy: DEStrategy::Rand1,
seed: None, seed: None,
} }
} }
@@ -261,9 +243,9 @@ impl DifferentialEvolutionSamplerBuilder {
/// Sets the mutation strategy. /// Sets the mutation strategy.
/// ///
/// Default: [`DifferentialEvolutionStrategy::Rand1`]. /// Default: [`DEStrategy::Rand1`].
#[must_use] #[must_use]
pub fn strategy(mut self, strategy: DifferentialEvolutionStrategy) -> Self { pub fn strategy(mut self, strategy: DEStrategy) -> Self {
self.strategy = strategy; self.strategy = strategy;
self self
} }
@@ -275,10 +257,10 @@ impl DifferentialEvolutionSamplerBuilder {
self self
} }
/// Builds the configured [`DifferentialEvolutionSampler`]. /// Builds the configured [`DESampler`].
#[must_use] #[must_use]
pub fn build(self) -> DifferentialEvolutionSampler { pub fn build(self) -> DESampler {
DifferentialEvolutionSampler { DESampler {
state: Mutex::new(State::new( state: Mutex::new(State::new(
self.population_size, self.population_size,
self.mutation_factor, self.mutation_factor,
@@ -345,7 +327,7 @@ struct State {
/// Crossover rate (CR). /// Crossover rate (CR).
crossover_rate: f64, crossover_rate: f64,
/// Mutation strategy. /// Mutation strategy.
strategy: DifferentialEvolutionStrategy, strategy: DEStrategy,
/// Current phase. /// Current phase.
phase: Phase, phase: Phase,
/// Discovered dimension info (populated during discovery). /// Discovered dimension info (populated during discovery).
@@ -383,7 +365,7 @@ impl State {
user_population_size: Option<usize>, user_population_size: Option<usize>,
mutation_factor: f64, mutation_factor: f64,
crossover_rate: f64, crossover_rate: f64,
strategy: DifferentialEvolutionStrategy, strategy: DEStrategy,
seed: Option<u64>, seed: Option<u64>,
) -> Self { ) -> Self {
let rng = seed.map_or_else(fastrand::Rng::new, fastrand::Rng::with_seed); let rng = seed.map_or_else(fastrand::Rng::new, fastrand::Rng::with_seed);
@@ -601,21 +583,21 @@ fn create_mutant_with_rng(state: &mut State, target_idx: usize, n_continuous: us
let pop_size = state.population_size; let pop_size = state.population_size;
match state.strategy { match state.strategy {
DifferentialEvolutionStrategy::Rand1 => { DEStrategy::Rand1 => {
let indices = select_random_indices(&mut state.rng, pop_size, 3, &[target_idx]); let indices = select_random_indices(&mut state.rng, pop_size, 3, &[target_idx]);
let (r1, r2, r3) = (indices[0], indices[1], indices[2]); let (r1, r2, r3) = (indices[0], indices[1], indices[2]);
(0..n_continuous) (0..n_continuous)
.map(|j| pop[r1][j] + f * (pop[r2][j] - pop[r3][j])) .map(|j| pop[r1][j] + f * (pop[r2][j] - pop[r3][j]))
.collect() .collect()
} }
DifferentialEvolutionStrategy::Best1 => { DEStrategy::Best1 => {
let indices = select_random_indices(&mut state.rng, pop_size, 2, &[target_idx]); let indices = select_random_indices(&mut state.rng, pop_size, 2, &[target_idx]);
let (r1, r2) = (indices[0], indices[1]); let (r1, r2) = (indices[0], indices[1]);
(0..n_continuous) (0..n_continuous)
.map(|j| pop[best_idx][j] + f * (pop[r1][j] - pop[r2][j])) .map(|j| pop[best_idx][j] + f * (pop[r1][j] - pop[r2][j]))
.collect() .collect()
} }
DifferentialEvolutionStrategy::CurrentToBest1 => { DEStrategy::CurrentToBest1 => {
let indices = select_random_indices(&mut state.rng, pop_size, 2, &[target_idx]); let indices = select_random_indices(&mut state.rng, pop_size, 2, &[target_idx]);
let (r1, r2) = (indices[0], indices[1]); let (r1, r2) = (indices[0], indices[1]);
(0..n_continuous) (0..n_continuous)
@@ -693,7 +675,7 @@ fn generate_initial_population(state: &mut State) -> Vec<Candidate> {
// Sampler trait implementation // Sampler trait implementation
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
impl Sampler for DifferentialEvolutionSampler { impl Sampler for DESampler {
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
fn sample( fn sample(
&self, &self,
@@ -968,7 +950,7 @@ mod tests {
#[test] #[test]
fn test_de_sampler_basic_float() { fn test_de_sampler_basic_float() {
let sampler = DifferentialEvolutionSampler::with_seed(42); let sampler = DESampler::with_seed(42);
let dist = Distribution::Float(FloatDistribution { let dist = Distribution::Float(FloatDistribution {
low: -5.0, low: -5.0,
high: 5.0, high: 5.0,
@@ -1000,7 +982,7 @@ mod tests {
}); });
let sample_values = |seed: u64| { let sample_values = |seed: u64| {
let sampler = DifferentialEvolutionSampler::with_seed(seed); let sampler = DESampler::with_seed(seed);
(0..20) (0..20)
.map(|i| sampler.sample(&dist, i, &[])) .map(|i| sampler.sample(&dist, i, &[]))
.collect::<Vec<_>>() .collect::<Vec<_>>()
@@ -1016,22 +998,16 @@ mod tests {
#[test] #[test]
fn test_de_strategy_default() { fn test_de_strategy_default() {
assert!(matches!( assert!(matches!(DEStrategy::default(), DEStrategy::Rand1));
DifferentialEvolutionStrategy::default(),
DifferentialEvolutionStrategy::Rand1
));
} }
#[test] #[test]
fn test_builder_defaults() { fn test_builder_defaults() {
let builder = DifferentialEvolutionSamplerBuilder::new(); let builder = DESamplerBuilder::new();
assert!(builder.population_size.is_none()); assert!(builder.population_size.is_none());
assert!((builder.mutation_factor - 0.8).abs() < f64::EPSILON); assert!((builder.mutation_factor - 0.8).abs() < f64::EPSILON);
assert!((builder.crossover_rate - 0.9).abs() < f64::EPSILON); assert!((builder.crossover_rate - 0.9).abs() < f64::EPSILON);
assert!(matches!( assert!(matches!(builder.strategy, DEStrategy::Rand1));
builder.strategy,
DifferentialEvolutionStrategy::Rand1
));
assert!(builder.seed.is_none()); assert!(builder.seed.is_none());
} }
} }
+39 -39
View File
@@ -1,6 +1,6 @@
//! Grid search sampler — exhaustive evaluation of discretized parameter spaces. //! Grid search sampler — exhaustive evaluation of discretized parameter spaces.
//! //!
//! [`GridSearchSampler`] divides each parameter range into a fixed number of //! [`GridSampler`] divides each parameter range into a fixed number of
//! evenly spaced points (or uses the explicit step size when defined) and //! evenly spaced points (or uses the explicit step size when defined) and
//! evaluates them sequentially. This guarantees complete coverage of the //! evaluates them sequentially. This guarantees complete coverage of the
//! search grid at the cost of scaling exponentially with the number of //! search grid at the cost of scaling exponentially with the number of
@@ -29,9 +29,9 @@
//! //!
//! ``` //! ```
//! use optimizer::prelude::*; //! use optimizer::prelude::*;
//! use optimizer::sampler::grid::GridSearchSampler; //! use optimizer::sampler::grid::GridSampler;
//! //!
//! let sampler = GridSearchSampler::builder().n_points_per_param(5).build(); //! let sampler = GridSampler::builder().n_points_per_param(5).build();
//! let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); //! let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
//! ``` //! ```
@@ -353,22 +353,22 @@ struct GridState {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use optimizer::sampler::grid::GridSearchSampler; /// use optimizer::sampler::grid::GridSampler;
/// ///
/// // Default: 10 points per parameter /// // Default: 10 points per parameter
/// let sampler = GridSearchSampler::new(); /// let sampler = GridSampler::new();
/// ///
/// // Custom grid density /// // Custom grid density
/// let sampler = GridSearchSampler::builder().n_points_per_param(20).build(); /// let sampler = GridSampler::builder().n_points_per_param(20).build();
/// ``` /// ```
pub struct GridSearchSampler { pub struct GridSampler {
/// Number of grid points per parameter (used when auto-discretizing). /// Number of grid points per parameter (used when auto-discretizing).
n_points_per_param: usize, n_points_per_param: usize,
/// Thread-safe internal state for tracking grid positions. /// Thread-safe internal state for tracking grid positions.
state: Mutex<GridState>, state: Mutex<GridState>,
} }
impl GridSearchSampler { impl GridSampler {
/// Creates a new grid search sampler with default settings. /// Creates a new grid search sampler with default settings.
/// ///
/// Default settings: /// Default settings:
@@ -386,9 +386,9 @@ impl GridSearchSampler {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use optimizer::sampler::grid::GridSearchSampler; /// use optimizer::sampler::grid::GridSampler;
/// ///
/// let sampler = GridSearchSampler::builder().n_points_per_param(20).build(); /// let sampler = GridSampler::builder().n_points_per_param(20).build();
/// ``` /// ```
#[must_use] #[must_use]
pub fn builder() -> GridSearchSamplerBuilder { pub fn builder() -> GridSearchSamplerBuilder {
@@ -396,13 +396,13 @@ impl GridSearchSampler {
} }
} }
impl Default for GridSearchSampler { impl Default for GridSampler {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl GridSearchSampler { impl GridSampler {
/// Returns `true` if all grid points for all tracked distributions have been sampled. /// Returns `true` if all grid points for all tracked distributions have been sampled.
/// ///
/// A distribution is considered exhausted when its `current_index` equals the number /// A distribution is considered exhausted when its `current_index` equals the number
@@ -416,9 +416,9 @@ impl GridSearchSampler {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use optimizer::sampler::grid::GridSearchSampler; /// use optimizer::sampler::grid::GridSampler;
/// ///
/// let sampler = GridSearchSampler::new(); /// let sampler = GridSampler::new();
/// // Initially exhausted (no distributions tracked yet) /// // Initially exhausted (no distributions tracked yet)
/// assert!(sampler.is_exhausted()); /// assert!(sampler.is_exhausted());
/// ``` /// ```
@@ -446,9 +446,9 @@ impl GridSearchSampler {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use optimizer::sampler::grid::GridSearchSampler; /// use optimizer::sampler::grid::GridSampler;
/// ///
/// let sampler = GridSearchSampler::new(); /// let sampler = GridSampler::new();
/// // No distributions tracked yet /// // No distributions tracked yet
/// assert_eq!(sampler.grid_size(), 0); /// assert_eq!(sampler.grid_size(), 0);
/// ``` /// ```
@@ -459,7 +459,7 @@ impl GridSearchSampler {
} }
} }
/// Builder for configuring a [`GridSearchSampler`]. /// Builder for configuring a [`GridSampler`].
/// ///
/// # Examples /// # Examples
/// ///
@@ -511,7 +511,7 @@ impl GridSearchSamplerBuilder {
self self
} }
/// Builds the configured [`GridSearchSampler`]. /// Builds the configured [`GridSampler`].
/// ///
/// # Examples /// # Examples
/// ///
@@ -523,8 +523,8 @@ impl GridSearchSamplerBuilder {
/// .build(); /// .build();
/// ``` /// ```
#[must_use] #[must_use]
pub fn build(self) -> GridSearchSampler { pub fn build(self) -> GridSampler {
GridSearchSampler { GridSampler {
n_points_per_param: self.n_points_per_param, n_points_per_param: self.n_points_per_param,
state: Mutex::new(GridState::default()), state: Mutex::new(GridState::default()),
} }
@@ -566,7 +566,7 @@ fn distribution_key(dist: &Distribution) -> String {
} }
} }
impl Sampler for GridSearchSampler { impl Sampler for GridSampler {
fn sample( fn sample(
&self, &self,
distribution: &Distribution, distribution: &Distribution,
@@ -876,7 +876,7 @@ mod tests {
#[test] #[test]
fn test_sampler_exhausts_after_expected_samples() { fn test_sampler_exhausts_after_expected_samples() {
let sampler = GridSearchSampler::new(); let sampler = GridSampler::new();
let dist = Distribution::Categorical(CategoricalDistribution { n_choices: 3 }); let dist = Distribution::Categorical(CategoricalDistribution { n_choices: 3 });
// Sample all 3 points // Sample all 3 points
@@ -890,7 +890,7 @@ mod tests {
#[test] #[test]
fn test_sampler_exhaustion_with_int_distribution() { fn test_sampler_exhaustion_with_int_distribution() {
let sampler = GridSearchSampler::builder().n_points_per_param(5).build(); let sampler = GridSampler::builder().n_points_per_param(5).build();
let dist = Distribution::Int(IntDistribution { let dist = Distribution::Int(IntDistribution {
low: 0, low: 0,
high: 100, high: 100,
@@ -910,7 +910,7 @@ mod tests {
#[test] #[test]
#[should_panic(expected = "GridSearchSampler: all grid points exhausted")] #[should_panic(expected = "GridSearchSampler: all grid points exhausted")]
fn test_sampler_panics_after_exhaustion() { fn test_sampler_panics_after_exhaustion() {
let sampler = GridSearchSampler::new(); let sampler = GridSampler::new();
let dist = Distribution::Categorical(CategoricalDistribution { n_choices: 2 }); let dist = Distribution::Categorical(CategoricalDistribution { n_choices: 2 });
// Sample all 2 points // Sample all 2 points
@@ -925,14 +925,14 @@ mod tests {
#[test] #[test]
fn test_is_exhausted_before_sampling() { fn test_is_exhausted_before_sampling() {
let sampler = GridSearchSampler::new(); let sampler = GridSampler::new();
// Newly created sampler is vacuously exhausted (no distributions tracked) // Newly created sampler is vacuously exhausted (no distributions tracked)
assert!(sampler.is_exhausted()); assert!(sampler.is_exhausted());
} }
#[test] #[test]
fn test_is_exhausted_during_sampling() { fn test_is_exhausted_during_sampling() {
let sampler = GridSearchSampler::new(); let sampler = GridSampler::new();
let dist = Distribution::Categorical(CategoricalDistribution { n_choices: 3 }); let dist = Distribution::Categorical(CategoricalDistribution { n_choices: 3 });
// After first sample, not exhausted // After first sample, not exhausted
@@ -950,7 +950,7 @@ mod tests {
#[test] #[test]
fn test_is_exhausted_multiple_distributions() { fn test_is_exhausted_multiple_distributions() {
let sampler = GridSearchSampler::new(); let sampler = GridSampler::new();
// Use different n_choices so they have different distribution keys // Use different n_choices so they have different distribution keys
let dist1 = Distribution::Categorical(CategoricalDistribution { n_choices: 2 }); let dist1 = Distribution::Categorical(CategoricalDistribution { n_choices: 2 });
let dist2 = Distribution::Categorical(CategoricalDistribution { n_choices: 3 }); let dist2 = Distribution::Categorical(CategoricalDistribution { n_choices: 3 });
@@ -976,7 +976,7 @@ mod tests {
#[test] #[test]
fn test_builder_default() { fn test_builder_default() {
let sampler = GridSearchSampler::builder().build(); let sampler = GridSampler::builder().build();
let dist = Distribution::Float(FloatDistribution { let dist = Distribution::Float(FloatDistribution {
low: 0.0, low: 0.0,
high: 1.0, high: 1.0,
@@ -993,7 +993,7 @@ mod tests {
#[test] #[test]
fn test_builder_custom_n_points() { fn test_builder_custom_n_points() {
let sampler = GridSearchSampler::builder().n_points_per_param(3).build(); let sampler = GridSampler::builder().n_points_per_param(3).build();
let dist = Distribution::Float(FloatDistribution { let dist = Distribution::Float(FloatDistribution {
low: 0.0, low: 0.0,
high: 1.0, high: 1.0,
@@ -1011,7 +1011,7 @@ mod tests {
#[test] #[test]
fn test_new_default() { fn test_new_default() {
let sampler = GridSearchSampler::new(); let sampler = GridSampler::new();
let dist = Distribution::Float(FloatDistribution { let dist = Distribution::Float(FloatDistribution {
low: 0.0, low: 0.0,
high: 1.0, high: 1.0,
@@ -1031,8 +1031,8 @@ mod tests {
#[test] #[test]
fn test_reproducibility_same_grid_order() { fn test_reproducibility_same_grid_order() {
// Two samplers with the same configuration should produce the same grid order // Two samplers with the same configuration should produce the same grid order
let sampler1 = GridSearchSampler::builder().n_points_per_param(5).build(); let sampler1 = GridSampler::builder().n_points_per_param(5).build();
let sampler2 = GridSearchSampler::builder().n_points_per_param(5).build(); let sampler2 = GridSampler::builder().n_points_per_param(5).build();
let dist = Distribution::Float(FloatDistribution { let dist = Distribution::Float(FloatDistribution {
low: 0.0, low: 0.0,
@@ -1051,8 +1051,8 @@ mod tests {
#[test] #[test]
fn test_reproducibility_int_distribution() { fn test_reproducibility_int_distribution() {
let sampler1 = GridSearchSampler::new(); let sampler1 = GridSampler::new();
let sampler2 = GridSearchSampler::new(); let sampler2 = GridSampler::new();
let dist = Distribution::Int(IntDistribution { let dist = Distribution::Int(IntDistribution {
low: 0, low: 0,
@@ -1073,8 +1073,8 @@ mod tests {
#[test] #[test]
fn test_reproducibility_categorical() { fn test_reproducibility_categorical() {
let sampler1 = GridSearchSampler::new(); let sampler1 = GridSampler::new();
let sampler2 = GridSearchSampler::new(); let sampler2 = GridSampler::new();
let dist = Distribution::Categorical(CategoricalDistribution { n_choices: 4 }); let dist = Distribution::Categorical(CategoricalDistribution { n_choices: 4 });
@@ -1091,13 +1091,13 @@ mod tests {
#[test] #[test]
fn test_grid_size_empty() { fn test_grid_size_empty() {
let sampler = GridSearchSampler::new(); let sampler = GridSampler::new();
assert_eq!(sampler.grid_size(), 0); assert_eq!(sampler.grid_size(), 0);
} }
#[test] #[test]
fn test_grid_size_single_distribution() { fn test_grid_size_single_distribution() {
let sampler = GridSearchSampler::builder().n_points_per_param(5).build(); let sampler = GridSampler::builder().n_points_per_param(5).build();
let dist = Distribution::Float(FloatDistribution { let dist = Distribution::Float(FloatDistribution {
low: 0.0, low: 0.0,
high: 1.0, high: 1.0,
@@ -1115,7 +1115,7 @@ mod tests {
#[test] #[test]
fn test_grid_size_multiple_distributions() { fn test_grid_size_multiple_distributions() {
let sampler = GridSearchSampler::builder().n_points_per_param(3).build(); let sampler = GridSampler::builder().n_points_per_param(3).build();
let dist1 = Distribution::Float(FloatDistribution { let dist1 = Distribution::Float(FloatDistribution {
low: 0.0, low: 0.0,
high: 1.0, high: 1.0,
+2 -2
View File
@@ -22,10 +22,10 @@ use std::collections::HashMap;
pub use bohb::BohbSampler; pub use bohb::BohbSampler;
#[cfg(feature = "cma-es")] #[cfg(feature = "cma-es")]
pub use cma_es::CmaEsSampler; pub use cma_es::CmaEsSampler;
pub use de::{DifferentialEvolutionSampler, DifferentialEvolutionStrategy}; pub use de::{DESampler, DEStrategy};
#[cfg(feature = "gp")] #[cfg(feature = "gp")]
pub use gp::GpSampler; pub use gp::GpSampler;
pub use grid::GridSearchSampler; pub use grid::GridSampler;
pub use moead::{Decomposition, MoeadSampler}; pub use moead::{Decomposition, MoeadSampler};
pub use motpe::MotpeSampler; pub use motpe::MotpeSampler;
pub use nsga2::Nsga2Sampler; pub use nsga2::Nsga2Sampler;
+16 -19
View File
@@ -1,9 +1,9 @@
use optimizer::prelude::*; use optimizer::prelude::*;
use optimizer::sampler::de::{DifferentialEvolutionSampler, DifferentialEvolutionStrategy}; use optimizer::sampler::de::{DESampler, DEStrategy};
#[test] #[test]
fn sphere_function() { fn sphere_function() {
let sampler = DifferentialEvolutionSampler::with_seed(42); let sampler = DESampler::with_seed(42);
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
let x = FloatParam::new(-5.0, 5.0).name("x"); let x = FloatParam::new(-5.0, 5.0).name("x");
@@ -27,10 +27,7 @@ fn sphere_function() {
#[test] #[test]
fn rosenbrock_function() { fn rosenbrock_function() {
let sampler = DifferentialEvolutionSampler::builder() let sampler = DESampler::builder().population_size(20).seed(42).build();
.population_size(20)
.seed(42)
.build();
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
let x = FloatParam::new(-5.0, 5.0).name("x"); let x = FloatParam::new(-5.0, 5.0).name("x");
@@ -55,7 +52,7 @@ fn rosenbrock_function() {
#[test] #[test]
fn rastrigin_function() { fn rastrigin_function() {
let sampler = DifferentialEvolutionSampler::builder() let sampler = DESampler::builder()
.population_size(30) .population_size(30)
.mutation_factor(0.7) .mutation_factor(0.7)
.crossover_rate(0.9) .crossover_rate(0.9)
@@ -88,7 +85,7 @@ fn rastrigin_function() {
#[test] #[test]
fn bounds_respected() { fn bounds_respected() {
let sampler = DifferentialEvolutionSampler::with_seed(123); let sampler = DESampler::with_seed(123);
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
let x = FloatParam::new(-2.0, 3.0).name("x"); let x = FloatParam::new(-2.0, 3.0).name("x");
@@ -112,8 +109,8 @@ fn bounds_respected() {
#[test] #[test]
fn strategy_best1() { fn strategy_best1() {
let sampler = DifferentialEvolutionSampler::builder() let sampler = DESampler::builder()
.strategy(DifferentialEvolutionStrategy::Best1) .strategy(DEStrategy::Best1)
.population_size(15) .population_size(15)
.seed(42) .seed(42)
.build(); .build();
@@ -140,8 +137,8 @@ fn strategy_best1() {
#[test] #[test]
fn strategy_current_to_best1() { fn strategy_current_to_best1() {
let sampler = DifferentialEvolutionSampler::builder() let sampler = DESampler::builder()
.strategy(DifferentialEvolutionStrategy::CurrentToBest1) .strategy(DEStrategy::CurrentToBest1)
.population_size(15) .population_size(15)
.seed(42) .seed(42)
.build(); .build();
@@ -168,7 +165,7 @@ fn strategy_current_to_best1() {
#[test] #[test]
fn mixed_params_float_and_categorical() { fn mixed_params_float_and_categorical() {
let sampler = DifferentialEvolutionSampler::with_seed(42); let sampler = DESampler::with_seed(42);
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
let x = FloatParam::new(-5.0, 5.0).name("x"); let x = FloatParam::new(-5.0, 5.0).name("x");
@@ -201,7 +198,7 @@ fn seeded_reproducibility() {
let y = FloatParam::new(-5.0, 5.0).name("y"); let y = FloatParam::new(-5.0, 5.0).name("y");
let run = |seed: u64| { let run = |seed: u64| {
let sampler = DifferentialEvolutionSampler::with_seed(seed); let sampler = DESampler::with_seed(seed);
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
study study
.optimize(50, |trial: &mut optimizer::Trial| { .optimize(50, |trial: &mut optimizer::Trial| {
@@ -224,7 +221,7 @@ fn different_seeds_different_results() {
let y = FloatParam::new(-5.0, 5.0).name("y"); let y = FloatParam::new(-5.0, 5.0).name("y");
let run = |seed: u64| { let run = |seed: u64| {
let sampler = DifferentialEvolutionSampler::with_seed(seed); let sampler = DESampler::with_seed(seed);
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
study study
.optimize(20, |trial: &mut optimizer::Trial| { .optimize(20, |trial: &mut optimizer::Trial| {
@@ -246,7 +243,7 @@ fn different_seeds_different_results() {
#[test] #[test]
fn single_dimension() { fn single_dimension() {
let sampler = DifferentialEvolutionSampler::with_seed(42); let sampler = DESampler::with_seed(42);
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
let x = FloatParam::new(-10.0, 10.0).name("x"); let x = FloatParam::new(-10.0, 10.0).name("x");
@@ -268,7 +265,7 @@ fn single_dimension() {
#[test] #[test]
fn integer_params() { fn integer_params() {
let sampler = DifferentialEvolutionSampler::with_seed(42); let sampler = DESampler::with_seed(42);
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
let n = IntParam::new(1, 20).name("n"); let n = IntParam::new(1, 20).name("n");
@@ -296,7 +293,7 @@ fn integer_params() {
#[test] #[test]
fn log_scale_params() { fn log_scale_params() {
let sampler = DifferentialEvolutionSampler::with_seed(42); let sampler = DESampler::with_seed(42);
let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler); let study: Study<f64> = Study::with_sampler(Direction::Minimize, sampler);
let lr = FloatParam::new(1e-5, 1.0).log_scale().name("lr"); let lr = FloatParam::new(1e-5, 1.0).log_scale().name("lr");
@@ -320,7 +317,7 @@ fn log_scale_params() {
#[test] #[test]
fn custom_mutation_and_crossover() { fn custom_mutation_and_crossover() {
let sampler = DifferentialEvolutionSampler::builder() let sampler = DESampler::builder()
.mutation_factor(0.5) .mutation_factor(0.5)
.crossover_rate(0.7) .crossover_rate(0.7)
.population_size(10) .population_size(10)