wafrift_evolution/evolution/crossover/
strategies.rs1use crate::evolution::Chromosome;
2use rand::Rng;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum CrossoverStrategy {
7 Uniform,
8 SinglePoint,
9 MultiPoint(usize),
10 OrderBased,
11}
12
13fn canonical_gene_names(parent_a: &Chromosome, parent_b: &Chromosome) -> Vec<String> {
14 let mut names = Vec::new();
15 for (name, _) in &parent_a.genes {
16 if !names.contains(name) {
17 names.push(name.clone());
18 }
19 }
20 for (name, _) in &parent_b.genes {
21 if !names.contains(name) {
22 names.push(name.clone());
23 }
24 }
25 names
26}
27
28fn gene_value_for(parent: &Chromosome, name: &str) -> Option<String> {
29 parent
30 .genes
31 .iter()
32 .find(|(gene_name, _)| gene_name == name)
33 .map(|(_, value)| value.clone())
34}
35
36fn value_from_parent(primary: &Chromosome, secondary: &Chromosome, name: &str) -> String {
37 gene_value_for(primary, name)
38 .or_else(|| gene_value_for(secondary, name))
39 .unwrap_or_else(|| String::from("None"))
40}
41
42fn build_child<F>(parent_a: &Chromosome, parent_b: &Chromosome, mut choose_a: F) -> Chromosome
43where
44 F: FnMut(usize, &str) -> bool,
45{
46 let child_genes = canonical_gene_names(parent_a, parent_b)
47 .into_iter()
48 .enumerate()
49 .map(|(index, name)| {
50 let value = if choose_a(index, &name) {
51 value_from_parent(parent_a, parent_b, &name)
52 } else {
53 value_from_parent(parent_b, parent_a, &name)
54 };
55 (name, value)
56 })
57 .collect();
58 Chromosome::new(child_genes)
59}
60
61#[must_use]
63pub fn uniform_crossover(
64 parent_a: &Chromosome,
65 parent_b: &Chromosome,
66 rng: &mut impl Rng,
67) -> Chromosome {
68 build_child(parent_a, parent_b, |_, _| rng.gen_bool(0.5))
69}
70
71#[must_use]
73pub fn single_point_crossover(
74 parent_a: &Chromosome,
75 parent_b: &Chromosome,
76 rng: &mut impl Rng,
77) -> Chromosome {
78 let gene_names = canonical_gene_names(parent_a, parent_b);
79 let max_len = gene_names.len();
80 if max_len == 0 {
81 return Chromosome::new(Vec::new());
82 }
83 let point = rng.gen_range(1..=max_len);
84 build_child(parent_a, parent_b, |index, _| index < point)
85}
86
87#[must_use]
89pub fn multi_point_crossover(
90 parent_a: &Chromosome,
91 parent_b: &Chromosome,
92 num_points: usize,
93 rng: &mut impl Rng,
94) -> Chromosome {
95 let gene_names = canonical_gene_names(parent_a, parent_b);
96 let max_len = gene_names.len();
97 if max_len == 0 {
98 return Chromosome::new(Vec::new());
99 }
100 if max_len == 1 {
105 return build_child(parent_a, parent_b, |_, _| true);
106 }
107 let mut points: Vec<usize> = (0..num_points.min(max_len))
108 .map(|_| rng.gen_range(1..max_len))
109 .collect();
110 points.sort_unstable();
111 points.dedup();
112 build_child(parent_a, parent_b, |index, _| {
113 points.iter().filter(|point| index >= **point).count() % 2 == 0
114 })
115}
116
117#[must_use]
119pub fn order_based_crossover(
120 parent_a: &Chromosome,
121 parent_b: &Chromosome,
122 rng: &mut impl Rng,
123) -> Chromosome {
124 build_child(parent_a, parent_b, |_, name| {
125 let prefer_a = parent_a
126 .genes
127 .iter()
128 .position(|(gene_name, _)| gene_name == name)
129 .unwrap_or(usize::MAX);
130 let prefer_b = parent_b
131 .genes
132 .iter()
133 .position(|(gene_name, _)| gene_name == name)
134 .unwrap_or(usize::MAX);
135 if prefer_a == prefer_b {
136 rng.gen_bool(0.5)
137 } else {
138 prefer_a < prefer_b
139 }
140 })
141}
142
143#[must_use]
145pub fn crossover_with_strategy(
146 parent_a: &Chromosome,
147 parent_b: &Chromosome,
148 strategy: CrossoverStrategy,
149 rng: &mut impl Rng,
150) -> Chromosome {
151 match strategy {
152 CrossoverStrategy::Uniform => uniform_crossover(parent_a, parent_b, rng),
153 CrossoverStrategy::SinglePoint => single_point_crossover(parent_a, parent_b, rng),
154 CrossoverStrategy::MultiPoint(points) => {
155 multi_point_crossover(parent_a, parent_b, points, rng)
156 }
157 CrossoverStrategy::OrderBased => order_based_crossover(parent_a, parent_b, rng),
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use rand::{SeedableRng, rngs::StdRng};
165
166 #[test]
167 fn empty_chromosome_crossover_returns_empty() {
168 let parent_a = Chromosome::new(Vec::new());
169 let parent_b = Chromosome::new(Vec::new());
170 let mut rng = StdRng::seed_from_u64(13);
171 let child = single_point_crossover(&parent_a, &parent_b, &mut rng);
172 assert!(child.genes.is_empty());
173 }
174
175 #[test]
176 fn single_gene_single_point_crossover_returns_valid_clone_shape() {
177 let parent_a = Chromosome::new(vec![("encoding".into(), "UrlEncode".into())]);
178 let parent_b = Chromosome::new(vec![("encoding".into(), "None".into())]);
179 let mut rng = StdRng::seed_from_u64(17);
180 let child = single_point_crossover(&parent_a, &parent_b, &mut rng);
181 assert_eq!(child.genes.len(), 1);
182 assert_eq!(child.genes[0].0, "encoding");
183 }
184
185 #[test]
186 fn crossover_with_identical_parents_matches_parent() {
187 let parent_a = Chromosome::new(vec![
188 ("encoding".into(), "UrlEncode".into()),
189 ("content_type".into(), "JsonNested".into()),
190 ]);
191 let parent_b = parent_a.clone();
192 let mut rng = StdRng::seed_from_u64(19);
193 let child = uniform_crossover(&parent_a, &parent_b, &mut rng);
194 assert_eq!(child.genes, parent_a.genes);
195 assert_eq!(child.genes, parent_b.genes);
196 }
197}