Skip to main content

math_audio_optimisation/
lshade.rs

1//! L-SHADE (Linear Population Size Reduction SHADE) Configuration
2//!
3//! L-SHADE extends SHADE with linear population size reduction:
4//! - Starts with large population for exploration
5//! - Gradually reduces population for focused exploitation
6
7/// L-SHADE specific configuration
8#[derive(Debug, Clone)]
9pub struct LShadeConfig {
10    /// Initial population size multiplier (default: 18)
11    /// NP_init = np_init * n_dim
12    pub np_init: usize,
13
14    /// Final population size (default: 4)
15    pub np_final: usize,
16
17    /// p parameter for pbest selection (default: 0.11)
18    /// Selects from top p*100% of population
19    pub p: f64,
20
21    /// Archive rate (default: 2.1)
22    /// Archive size = arc_rate * NP_current
23    pub arc_rate: f64,
24
25    /// Memory size for parameter adaptation (default: 6)
26    pub memory_size: usize,
27}
28
29impl Default for LShadeConfig {
30    fn default() -> Self {
31        Self {
32            np_init: 18,
33            np_final: 4,
34            p: 0.11,
35            arc_rate: 2.1,
36            memory_size: 6,
37        }
38    }
39}
40
41impl LShadeConfig {
42    /// Compute initial population size
43    pub fn initial_population_size(&self, n_dim: usize) -> usize {
44        (self.np_init * n_dim).max(10)
45    }
46
47    /// Compute current population size based on progress
48    /// Uses linear reduction formula from L-SHADE paper
49    pub fn current_population_size(&self, n_dim: usize, nfev: usize, max_nfev: usize) -> usize {
50        let np_init = self.initial_population_size(n_dim) as f64;
51        let np_final = self.np_final as f64;
52
53        let progress = (nfev as f64 / max_nfev as f64).min(1.0);
54        let np = np_final + (np_init - np_final) * (1.0 - progress);
55
56        (np.round() as usize).max(self.np_final)
57    }
58
59    /// Compute current archive size
60    pub fn current_archive_size(&self, current_np: usize) -> usize {
61        (self.arc_rate * current_np as f64).ceil() as usize
62    }
63
64    /// Compute pbest size for current population
65    pub fn pbest_size(&self, current_np: usize) -> usize {
66        ((self.p * current_np as f64).ceil() as usize)
67            .max(1)
68            .min(current_np)
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_population_reduction() {
78        let config = LShadeConfig::default();
79        let n_dim = 10;
80
81        let np_init = config.initial_population_size(n_dim);
82        assert_eq!(np_init, 180);
83
84        let np_start = config.current_population_size(n_dim, 0, 10000);
85        assert!(np_start >= np_init - 1);
86
87        let np_end = config.current_population_size(n_dim, 10000, 10000);
88        assert_eq!(np_end, config.np_final);
89    }
90
91    #[test]
92    fn test_archive_size() {
93        let config = LShadeConfig::default();
94        assert_eq!(config.current_archive_size(100), 210);
95    }
96}