1use crate::meta::{CrossoverParams, MutationParams};
2use rand::rngs::StdRng;
3use rand_distr::{Cauchy, Distribution};
4
5const META_PARAMS_MUTATION_EXPONENT_SCALE: f64 = 0.1;
6const META_PARAMS_MUTATION_RESCALE_FLOOR: f64 = 1e-12;
7const META_PARAMS_MUTATION_RESCALE_CEIL: f64 = 1e12;
8
9fn rescale(value: f64, rng: &mut StdRng) -> f64 {
10 let exponent: f64 = Cauchy::new(0.0, META_PARAMS_MUTATION_EXPONENT_SCALE)
11 .unwrap()
12 .sample(rng);
13
14 let factor = 10.0f64.powf(exponent);
15
16 value
17 * factor
18 .max(META_PARAMS_MUTATION_RESCALE_FLOOR)
19 .min(META_PARAMS_MUTATION_RESCALE_CEIL)
20}
21
22fn rescale_prob(prob: f64, rng: &mut StdRng) -> f64 {
23 rescale(prob, rng).min(1.0)
24}
25
26pub fn create_exploratory(rng: &mut StdRng) -> (CrossoverParams, MutationParams) {
27 let crossover_params = CrossoverParams {
28 crossover_prob: 0.5,
29 selection_pressure: 0.5,
30 };
31
32 let mutation_params = MutationParams {
33 mutation_prob: 0.5,
34 mutation_scale: 1.0,
35 };
36
37 mutate(crossover_params, mutation_params, rng)
38}
39
40pub fn mutate(
41 crossover_params: CrossoverParams,
42 mutation_params: MutationParams,
43 rng: &mut StdRng,
44) -> (CrossoverParams, MutationParams) {
45 let crossover_params = CrossoverParams {
46 crossover_prob: rescale_prob(crossover_params.crossover_prob, rng),
47 selection_pressure: rescale_prob(crossover_params.selection_pressure, rng),
48 };
49
50 let mutation_params = MutationParams {
51 mutation_prob: rescale_prob(mutation_params.mutation_prob, rng),
52 mutation_scale: rescale(mutation_params.mutation_scale, rng),
53 };
54
55 (crossover_params, mutation_params)
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use float_cmp::assert_approx_eq;
62 use rand::SeedableRng;
63
64 fn assert_min_less_than(vals: &[f64], cmp: f64) {
65 assert!(*vals.iter().min_by(|a, b| a.total_cmp(b)).unwrap() < cmp);
66 }
67
68 fn assert_max_greater_than(vals: &[f64], cmp: f64) {
69 assert!(*vals.iter().max_by(|a, b| a.total_cmp(b)).unwrap() > cmp);
70 }
71
72 fn assert_max_equal_to(vals: &[f64], cmp: f64) {
73 assert_approx_eq!(
74 f64,
75 *vals.iter().max_by(|a, b| a.total_cmp(b)).unwrap(),
76 cmp
77 );
78 }
79
80 #[test]
81 fn mutate_covers_orders_of_magnitude() {
82 let mut rng = StdRng::seed_from_u64(0);
83
84 let crossover_params = CrossoverParams {
85 crossover_prob: 0.5,
86 selection_pressure: 0.5,
87 };
88
89 let mutation_params = MutationParams {
90 mutation_prob: 0.5,
91 mutation_scale: 0.5,
92 };
93
94 let mut crossover_probs = Vec::new();
95 let mut selection_pressures = Vec::new();
96 let mut mutation_probs = Vec::new();
97 let mut mutation_scales = Vec::new();
98
99 for _ in 0..100 {
100 let (crossover_params, mutation_params) =
101 mutate(crossover_params.clone(), mutation_params.clone(), &mut rng);
102
103 crossover_probs.push(crossover_params.crossover_prob);
104 selection_pressures.push(crossover_params.selection_pressure);
105 mutation_probs.push(mutation_params.mutation_prob);
106 mutation_scales.push(mutation_params.mutation_scale);
107 }
108
109 for vals in [crossover_probs, selection_pressures, mutation_probs] {
110 assert_min_less_than(&vals, 0.1);
111 assert_max_equal_to(&vals, 1.0);
112 }
113
114 assert_min_less_than(&mutation_scales, 0.1);
115 assert_max_greater_than(&mutation_scales, 10.0);
116 }
117
118 #[test]
119 fn exploratory_covers_orders_of_magnitude() {
120 let mut rng = StdRng::seed_from_u64(0);
121
122 let mut crossover_probs = Vec::new();
123 let mut selection_pressures = Vec::new();
124 let mut mutation_probs = Vec::new();
125 let mut mutation_scales = Vec::new();
126
127 for _ in 0..100 {
128 let (crossover_params, mutation_params) = create_exploratory(&mut rng);
129
130 crossover_probs.push(crossover_params.crossover_prob);
131 selection_pressures.push(crossover_params.selection_pressure);
132 mutation_probs.push(mutation_params.mutation_prob);
133 mutation_scales.push(mutation_params.mutation_scale);
134 }
135
136 for vals in [crossover_probs, selection_pressures, mutation_probs] {
137 assert_min_less_than(&vals, 0.1);
138 assert_max_equal_to(&vals, 1.0);
139 }
140
141 assert_min_less_than(&mutation_scales, 0.5);
142 assert_max_greater_than(&mutation_scales, 5.0);
143 }
144}