Skip to main content

des_sim/modeling/sampler/instance/
uniform.rs

1//! The `uniform` module provides the `UniformSampler`, a `DurationSampler`
2//! that generates durations following a continuous uniform distribution.
3//!
4//! This sampler is useful for modeling scenarios where any value within a given
5//! range is equally likely, such as random delays or resource allocation times
6//! with no particular bias.
7
8use crate::modeling::sampler::{DurationSampler, PendingDuration};
9use crate::primitive::time::SimTime;
10use rand::Rng;
11use rand::distr::{Distribution, Uniform, uniform};
12
13/// A sampler that draws from a continuous uniform distribution within [lower_bound, higher_bound).
14#[derive(Debug, Clone)]
15pub struct UniformSampler {
16    dist: Uniform<f64>,
17}
18
19impl DurationSampler for UniformSampler {
20    fn sample(&mut self, rng: &mut dyn Rng, _current_tick: SimTime) -> PendingDuration {
21        PendingDuration::new(self.dist.sample(rng))
22    }
23}
24
25impl UniformSampler {
26    /// Creates a new `UniformSampler` with the given range.
27    /// Returns an error if the range is empty or if values are non-finite.
28    pub fn new(lower_bound: f64, higher_bound: f64) -> Result<UniformSampler, uniform::Error> {
29        let uniform = Uniform::new(lower_bound, higher_bound)?;
30
31        Ok(UniformSampler { dist: uniform })
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38    use rand::SeedableRng;
39    use rand::rngs::SmallRng;
40
41    #[test]
42    fn test_uniform_sampler() {
43        let mut rng = SmallRng::seed_from_u64(2);
44        let lower_bound = 5.0;
45        let higher_bound = 15.0;
46        let mut sampler = UniformSampler::new(lower_bound, higher_bound).unwrap();
47
48        let mut samples = Vec::new();
49        for _ in 0..10000 {
50            samples.push(sampler.sample(&mut rng, SimTime::from_ticks(0)).raw_value());
51        }
52
53        // Check if all samples are within the specified range
54        assert!(
55            samples
56                .iter()
57                .all(|&x| x >= lower_bound && x < higher_bound)
58        );
59
60        let sample_mean: f64 = samples.iter().sum::<f64>() / samples.len() as f64;
61        let expected_mean = (lower_bound + higher_bound) / 2.0;
62
63        // Check if the sampled mean is close to the expected mean
64        assert!(
65            (sample_mean - expected_mean).abs() < 0.1,
66            "Mean: {}, Expected: {}",
67            sample_mean,
68            expected_mean
69        );
70    }
71
72    #[test]
73    fn test_uniform_sampler_invalid_bounds() {
74        // Lower bound must be less than higher bound
75        let sampler_empty_range = UniformSampler::new(10.0, 5.0);
76        assert_eq!(sampler_empty_range.err(), Some(uniform::Error::EmptyRange));
77    }
78
79    #[test]
80    fn test_uniform_sampler_invalid_infinite_bounds() {
81        // Bounds must be finite
82        let sampler_infinite_range = UniformSampler::new(10.0, f64::INFINITY);
83        assert_eq!(
84            sampler_infinite_range.err(),
85            Some(uniform::Error::NonFinite)
86        );
87    }
88}