wafrift_evolution/evolution/crossover/
mutation.rs1use crate::evolution::{Chromosome, GenePool};
2use crate::lineage::MutationOp;
3use rand::Rng;
4use wafrift_types::pick::pick_from_rng;
5
6pub fn mutate_with_log(
8 chromosome: &mut Chromosome,
9 gene_pool: &GenePool,
10 mutation_rate: f64,
11 rng: &mut impl Rng,
12) -> Vec<MutationOp> {
13 let mut log = Vec::new();
14 for gene in &mut chromosome.genes {
15 if rng.gen_bool(mutation_rate)
16 && let Some(value) = gene_pool.random_value(&gene.0, rng)
17 && value != gene.1
18 {
19 log.push(MutationOp {
20 gene_name: gene.0.clone(),
21 from: std::mem::replace(&mut gene.1, value.clone()),
22 to: value,
23 operator: "value_mutation".into(),
24 });
25 }
26 }
27 log
28}
29
30pub fn mutate(
32 chromosome: &mut Chromosome,
33 gene_pool: &GenePool,
34 mutation_rate: f64,
35 rng: &mut impl Rng,
36) {
37 let _ = mutate_with_log(chromosome, gene_pool, mutation_rate, rng);
38}
39
40pub fn structural_add_mutation(
42 chromosome: &mut Chromosome,
43 gene_pool: &GenePool,
44 add_rate: f64,
45 rng: &mut impl Rng,
46) {
47 let pool_names: Vec<&str> = gene_pool.gene_names();
48 let missing_names: Vec<&str> = pool_names
49 .into_iter()
50 .filter(|name| !chromosome.has_gene(name))
51 .collect();
52
53 if !missing_names.is_empty() && rng.gen_bool(add_rate) {
54 let name = pick_from_rng(&missing_names, missing_names[0], rng);
55 if let Some(value) = gene_pool.random_value(name, rng) {
56 chromosome.genes.push((name.to_string(), value));
57 }
58 }
59}
60
61pub const ESSENTIAL_GENES: &[&str] = &["encoding"];
63
64pub fn structural_remove_mutation(
66 chromosome: &mut Chromosome,
67 remove_rate: f64,
68 min_genes: usize,
69 rng: &mut impl Rng,
70) {
71 if chromosome.genes.len() > min_genes && rng.gen_bool(remove_rate) {
72 let removable: Vec<usize> = chromosome
73 .genes
74 .iter()
75 .enumerate()
76 .filter(|(_, (name, _))| !ESSENTIAL_GENES.contains(&name.as_str()))
77 .map(|(i, _)| i)
78 .collect();
79
80 if !removable.is_empty() {
81 let idx = pick_from_rng(&removable, removable[0], rng);
82 chromosome.genes.remove(idx);
83 }
84 }
85}
86
87pub fn swap_mutation(chromosome: &mut Chromosome, swap_rate: f64, rng: &mut impl Rng) {
89 if chromosome.genes.len() < 2 || !rng.gen_bool(swap_rate) {
90 return;
91 }
92 let idx_a = rng.gen_range(0..chromosome.genes.len());
93 let idx_b = rng.gen_range(0..chromosome.genes.len());
94 if idx_a != idx_b {
95 chromosome.genes.swap(idx_a, idx_b);
96 }
97}
98
99pub fn scramble_mutation(chromosome: &mut Chromosome, scramble_rate: f64, rng: &mut impl Rng) {
101 if chromosome.genes.len() < 3 || !rng.gen_bool(scramble_rate) {
102 return;
103 }
104 let start = rng.gen_range(0..chromosome.genes.len() - 1);
105 let end = rng.gen_range(start + 1..chromosome.genes.len());
106 for i in (start + 1..end).rev() {
107 let j = rng.gen_range(start..=i);
108 chromosome.genes.swap(i, j);
109 }
110}
111
112pub fn comprehensive_mutate(
114 chromosome: &mut Chromosome,
115 gene_pool: &GenePool,
116 value_mutation_rate: f64,
117 structural_add_rate: f64,
118 structural_remove_rate: f64,
119 min_genes: usize,
120 rng: &mut impl Rng,
121) -> Vec<MutationOp> {
122 let log = mutate_with_log(chromosome, gene_pool, value_mutation_rate, rng);
123 structural_add_mutation(chromosome, gene_pool, structural_add_rate, rng);
124 structural_remove_mutation(chromosome, structural_remove_rate, min_genes, rng);
125 swap_mutation(chromosome, 0.1, rng);
126 scramble_mutation(chromosome, 0.05, rng);
127 log
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use rand::{SeedableRng, rngs::StdRng};
134
135 #[test]
136 fn empty_chromosome_mutation_is_noop() {
137 let mut chromosome = Chromosome::new(Vec::new());
138 let gene_pool = GenePool::default_wafrift();
139 let mut rng = StdRng::seed_from_u64(23);
140 let log = mutate_with_log(&mut chromosome, &gene_pool, 1.0, &mut rng);
141 assert!(chromosome.genes.is_empty());
142 assert!(log.is_empty());
143 }
144}