use rand::{Rng, RngCore};
use crate::traits::SearchSpace;
#[derive(Debug, Clone)]
pub struct ContinuousSpace {
bounds: Vec<(f64, f64)>,
}
impl ContinuousSpace {
pub fn new(bounds: Vec<(f64, f64)>) -> Self {
for (i, (lo, hi)) in bounds.iter().enumerate() {
assert!(lo <= hi, "invalid bound in dimension {i}: {lo} > {hi}");
}
Self { bounds }
}
pub fn uniform(dim: usize, lo: f64, hi: f64) -> Self {
Self::new(vec![(lo, hi); dim])
}
pub fn bounds(&self) -> &[(f64, f64)] {
&self.bounds
}
}
impl SearchSpace for ContinuousSpace {
type Scalar = f64;
fn dim(&self) -> usize {
self.bounds.len()
}
fn sample(&self, rng: &mut dyn RngCore) -> Vec<f64> {
self.bounds
.iter()
.map(|&(lo, hi)| rng.gen_range(lo..=hi))
.collect()
}
fn sample_velocity(&self, rng: &mut dyn RngCore) -> Vec<f64> {
self.bounds
.iter()
.map(|&(lo, hi)| {
let range = hi - lo;
rng.gen_range(-range..=range)
})
.collect()
}
fn clamp(&self, position: &mut [f64]) {
for (x, &(lo, hi)) in position.iter_mut().zip(&self.bounds) {
*x = x.clamp(lo, hi);
}
}
fn enforce_bounds(
&self,
position: &mut [f64],
velocity: &mut [f64],
handling: crate::traits::BoundaryHandling,
rng: &mut dyn RngCore,
) {
super::apply_boundary(position, velocity, |i| self.bounds[i], handling, rng);
}
fn decode(&self, raw: &[f64]) -> Vec<f64> {
raw.to_vec() }
fn span(&self) -> Vec<(f64, f64)> {
self.bounds.clone()
}
}