math_audio_differential_evolution/
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)]
50mod tests {
51 use crate::{AdaptiveConfig, DEConfigBuilder, Mutation, Strategy, differential_evolution};
52 use math_audio_test_functions::quadratic;
53
54 #[test]
55 fn test_adaptive_basic() {
56 let bounds = [(-5.0, 5.0), (-5.0, 5.0)];
58
59 let adaptive_config = AdaptiveConfig {
61 adaptive_mutation: true,
62 wls_enabled: false, w_max: 0.9,
64 w_min: 0.1,
65 ..AdaptiveConfig::default()
66 };
67
68 let config = DEConfigBuilder::new()
69 .seed(42)
70 .maxiter(100)
71 .popsize(30)
72 .strategy(Strategy::AdaptiveBin)
73 .mutation(Mutation::Adaptive { initial_f: 0.8 })
74 .adaptive(adaptive_config)
75 .build()
76 .expect("popsize must be >= 4");
77
78 let result = differential_evolution(&quadratic, &bounds, config)
79 .expect("optimization should succeed");
80
81 assert!(
83 result.fun < 1e-3,
84 "Adaptive DE should converge: f={}",
85 result.fun
86 );
87
88 for &xi in result.x.iter() {
90 assert!(
91 xi.abs() < 0.5,
92 "Solution component should be close to 0: {}",
93 xi
94 );
95 }
96 }
97
98 #[test]
99 fn test_adaptive_with_wls() {
100 let bounds = [(-5.0, 5.0), (-5.0, 5.0)];
102
103 let adaptive_config = AdaptiveConfig {
104 adaptive_mutation: true,
105 wls_enabled: true,
106 wls_prob: 0.2, wls_scale: 0.1,
108 ..AdaptiveConfig::default()
109 };
110
111 let config = DEConfigBuilder::new()
112 .seed(123)
113 .maxiter(200)
114 .popsize(40)
115 .strategy(Strategy::AdaptiveExp)
116 .adaptive(adaptive_config)
117 .build()
118 .expect("popsize must be >= 4");
119
120 let result = differential_evolution(&quadratic, &bounds, config)
121 .expect("optimization should succeed");
122
123 assert!(
125 result.fun < 1e-4,
126 "Adaptive DE with WLS should converge well: f={}",
127 result.fun
128 );
129
130 for &xi in result.x.iter() {
131 assert!(
132 xi.abs() < 0.2,
133 "Solution should be very close to 0 with WLS: {}",
134 xi
135 );
136 }
137 }
138}