Skip to main content

wafrift_evolution/evolution/crossover/
mutation.rs

1use crate::evolution::{Chromosome, GenePool};
2use crate::lineage::MutationOp;
3use rand::Rng;
4
5/// Mutate a chromosome and return a log of applied mutations.
6pub fn mutate_with_log(
7    chromosome: &mut Chromosome,
8    gene_pool: &GenePool,
9    mutation_rate: f64,
10    rng: &mut impl Rng,
11) -> Vec<MutationOp> {
12    let mut log = Vec::new();
13    for gene in &mut chromosome.genes {
14        if rng.gen_bool(mutation_rate)
15            && let Some(value) = gene_pool.random_value(&gene.0, rng)
16            && value != gene.1
17        {
18            log.push(MutationOp {
19                gene_name: gene.0.clone(),
20                from: std::mem::replace(&mut gene.1, value.clone()),
21                to: value,
22                operator: "value_mutation".into(),
23            });
24        }
25    }
26    log
27}
28
29/// Basic mutation: randomly changes gene values.
30pub fn mutate(
31    chromosome: &mut Chromosome,
32    gene_pool: &GenePool,
33    mutation_rate: f64,
34    rng: &mut impl Rng,
35) {
36    let _ = mutate_with_log(chromosome, gene_pool, mutation_rate, rng);
37}
38
39/// Structural mutation: add new genes from the gene pool.
40pub fn structural_add_mutation(
41    chromosome: &mut Chromosome,
42    gene_pool: &GenePool,
43    add_rate: f64,
44    rng: &mut impl Rng,
45) {
46    let pool_names: Vec<&str> = gene_pool.gene_names();
47    let missing_names: Vec<&str> = pool_names
48        .into_iter()
49        .filter(|name| !chromosome.has_gene(name))
50        .collect();
51
52    if !missing_names.is_empty() && rng.gen_bool(add_rate) {
53        let name = missing_names[rng.gen_range(0..missing_names.len())];
54        if let Some(value) = gene_pool.random_value(name, rng) {
55            chromosome.genes.push((name.to_string(), value));
56        }
57    }
58}
59
60/// Essential gene names that should not be removed.
61pub const ESSENTIAL_GENES: &[&str] = &["encoding"];
62
63/// Structural mutation: remove genes from the chromosome.
64pub fn structural_remove_mutation(
65    chromosome: &mut Chromosome,
66    remove_rate: f64,
67    min_genes: usize,
68    rng: &mut impl Rng,
69) {
70    if chromosome.genes.len() > min_genes && rng.gen_bool(remove_rate) {
71        let removable: Vec<usize> = chromosome
72            .genes
73            .iter()
74            .enumerate()
75            .filter(|(_, (name, _))| !ESSENTIAL_GENES.contains(&name.as_str()))
76            .map(|(i, _)| i)
77            .collect();
78
79        if !removable.is_empty() {
80            let idx = removable[rng.gen_range(0..removable.len())];
81            chromosome.genes.remove(idx);
82        }
83    }
84}
85
86/// Swap mutation: exchanges values between two genes.
87pub fn swap_mutation(chromosome: &mut Chromosome, swap_rate: f64, rng: &mut impl Rng) {
88    if chromosome.genes.len() < 2 || !rng.gen_bool(swap_rate) {
89        return;
90    }
91    let idx_a = rng.gen_range(0..chromosome.genes.len());
92    let idx_b = rng.gen_range(0..chromosome.genes.len());
93    if idx_a != idx_b {
94        chromosome.genes.swap(idx_a, idx_b);
95    }
96}
97
98/// Scramble mutation: randomly shuffles a subset of genes.
99pub fn scramble_mutation(chromosome: &mut Chromosome, scramble_rate: f64, rng: &mut impl Rng) {
100    if chromosome.genes.len() < 3 || !rng.gen_bool(scramble_rate) {
101        return;
102    }
103    let start = rng.gen_range(0..chromosome.genes.len() - 1);
104    let end = rng.gen_range(start + 1..chromosome.genes.len());
105    for i in (start + 1..end).rev() {
106        let j = rng.gen_range(start..=i);
107        chromosome.genes.swap(i, j);
108    }
109}
110
111/// Complete mutation operator applying all mutation types.
112pub fn comprehensive_mutate(
113    chromosome: &mut Chromosome,
114    gene_pool: &GenePool,
115    value_mutation_rate: f64,
116    structural_add_rate: f64,
117    structural_remove_rate: f64,
118    min_genes: usize,
119    rng: &mut impl Rng,
120) -> Vec<MutationOp> {
121    let log = mutate_with_log(chromosome, gene_pool, value_mutation_rate, rng);
122    structural_add_mutation(chromosome, gene_pool, structural_add_rate, rng);
123    structural_remove_mutation(chromosome, structural_remove_rate, min_genes, rng);
124    swap_mutation(chromosome, 0.1, rng);
125    scramble_mutation(chromosome, 0.05, rng);
126    log
127}