math_audio_optimisation/
mutant_adaptive.rs1use ndarray::{Array1, Array2};
2use rand::Rng;
3use rand::seq::SliceRandom;
4
5use crate::mutant_rand1::mutant_rand1;
6
7pub(crate) fn mutant_adaptive<R: Rng + ?Sized>(
10 i: usize,
11 pop: &Array2<f64>,
12 sorted_indices: &[usize],
13 w: f64,
14 f: f64,
15 rng: &mut R,
16) -> Array1<f64> {
17 let w_size = ((w * pop.nrows() as f64) as usize)
19 .max(1)
20 .min(pop.nrows() - 1);
21
22 let top_indices = &sorted_indices[0..w_size];
24 let gr_better_idx = top_indices[rng.random_range(0..w_size)];
25 let mut available: Vec<usize> = (0..pop.nrows())
27 .filter(|&idx| idx != i && idx != gr_better_idx)
28 .collect();
29 available.shuffle(rng);
30
31 if available.len() < 2 {
32 return mutant_rand1(i, pop, f, rng);
34 }
35
36 let r1 = available[0];
37 let r2 = available[1];
38
39 pop.row(i).to_owned()
42 + &((pop.row(gr_better_idx).to_owned() - pop.row(i).to_owned() + pop.row(r1).to_owned()
43 - pop.row(r2).to_owned())
44 * f)
45}
46
47#[cfg(test)]
49mod tests {
50 use crate::{AdaptiveConfig, DEConfigBuilder, Mutation, Strategy, differential_evolution};
51 use math_audio_test_functions::quadratic;
52
53 #[test]
54 fn test_adaptive_basic() {
55 let bounds = [(-5.0, 5.0), (-5.0, 5.0)];
57
58 let adaptive_config = AdaptiveConfig {
60 adaptive_mutation: true,
61 wls_enabled: false, w_max: 0.9,
63 w_min: 0.1,
64 ..AdaptiveConfig::default()
65 };
66
67 let config = DEConfigBuilder::new()
68 .seed(42)
69 .maxiter(100)
70 .popsize(30)
71 .strategy(Strategy::AdaptiveBin)
72 .mutation(Mutation::Adaptive { initial_f: 0.8 })
73 .adaptive(adaptive_config)
74 .build()
75 .expect("popsize must be >= 4");
76
77 let result = differential_evolution(&quadratic, &bounds, config)
78 .expect("optimization should succeed");
79
80 assert!(
82 result.fun < 1e-3,
83 "Adaptive DE should converge: f={}",
84 result.fun
85 );
86
87 for &xi in result.x.iter() {
89 assert!(
90 xi.abs() < 0.5,
91 "Solution component should be close to 0: {}",
92 xi
93 );
94 }
95 }
96
97 #[test]
98 fn test_adaptive_with_wls() {
99 let bounds = [(-5.0, 5.0), (-5.0, 5.0)];
101
102 let adaptive_config = AdaptiveConfig {
103 adaptive_mutation: true,
104 wls_enabled: true,
105 wls_prob: 0.2, wls_scale: 0.1,
107 ..AdaptiveConfig::default()
108 };
109
110 let config = DEConfigBuilder::new()
111 .seed(123)
112 .maxiter(200)
113 .popsize(40)
114 .strategy(Strategy::AdaptiveExp)
115 .adaptive(adaptive_config)
116 .build()
117 .expect("popsize must be >= 4");
118
119 let result = differential_evolution(&quadratic, &bounds, config)
120 .expect("optimization should succeed");
121
122 assert!(
124 result.fun < 1e-4,
125 "Adaptive DE with WLS should converge well: f={}",
126 result.fun
127 );
128
129 for &xi in result.x.iter() {
130 assert!(
131 xi.abs() < 0.2,
132 "Solution should be very close to 0 with WLS: {}",
133 xi
134 );
135 }
136 }
137}