use crate::evolution::{Chromosome, GenePool};
use crate::lineage::MutationOp;
use rand::Rng;
pub fn mutate_with_log(
chromosome: &mut Chromosome,
gene_pool: &GenePool,
mutation_rate: f64,
rng: &mut impl Rng,
) -> Vec<MutationOp> {
let mut log = Vec::new();
for gene in &mut chromosome.genes {
if rng.gen_bool(mutation_rate)
&& let Some(value) = gene_pool.random_value(&gene.0, rng)
&& value != gene.1
{
log.push(MutationOp {
gene_name: gene.0.clone(),
from: std::mem::replace(&mut gene.1, value.clone()),
to: value,
operator: "value_mutation".into(),
});
}
}
log
}
pub fn mutate(
chromosome: &mut Chromosome,
gene_pool: &GenePool,
mutation_rate: f64,
rng: &mut impl Rng,
) {
let _ = mutate_with_log(chromosome, gene_pool, mutation_rate, rng);
}
pub fn structural_add_mutation(
chromosome: &mut Chromosome,
gene_pool: &GenePool,
add_rate: f64,
rng: &mut impl Rng,
) {
let pool_names: Vec<&str> = gene_pool.gene_names();
let missing_names: Vec<&str> = pool_names
.into_iter()
.filter(|name| !chromosome.has_gene(name))
.collect();
if !missing_names.is_empty() && rng.gen_bool(add_rate) {
let name = missing_names[rng.gen_range(0..missing_names.len())];
if let Some(value) = gene_pool.random_value(name, rng) {
chromosome.genes.push((name.to_string(), value));
}
}
}
pub const ESSENTIAL_GENES: &[&str] = &["encoding"];
pub fn structural_remove_mutation(
chromosome: &mut Chromosome,
remove_rate: f64,
min_genes: usize,
rng: &mut impl Rng,
) {
if chromosome.genes.len() > min_genes && rng.gen_bool(remove_rate) {
let removable: Vec<usize> = chromosome
.genes
.iter()
.enumerate()
.filter(|(_, (name, _))| !ESSENTIAL_GENES.contains(&name.as_str()))
.map(|(i, _)| i)
.collect();
if !removable.is_empty() {
let idx = removable[rng.gen_range(0..removable.len())];
chromosome.genes.remove(idx);
}
}
}
pub fn swap_mutation(chromosome: &mut Chromosome, swap_rate: f64, rng: &mut impl Rng) {
if chromosome.genes.len() < 2 || !rng.gen_bool(swap_rate) {
return;
}
let idx_a = rng.gen_range(0..chromosome.genes.len());
let idx_b = rng.gen_range(0..chromosome.genes.len());
if idx_a != idx_b {
chromosome.genes.swap(idx_a, idx_b);
}
}
pub fn scramble_mutation(chromosome: &mut Chromosome, scramble_rate: f64, rng: &mut impl Rng) {
if chromosome.genes.len() < 3 || !rng.gen_bool(scramble_rate) {
return;
}
let start = rng.gen_range(0..chromosome.genes.len() - 1);
let end = rng.gen_range(start + 1..chromosome.genes.len());
for i in (start + 1..end).rev() {
let j = rng.gen_range(start..=i);
chromosome.genes.swap(i, j);
}
}
pub fn comprehensive_mutate(
chromosome: &mut Chromosome,
gene_pool: &GenePool,
value_mutation_rate: f64,
structural_add_rate: f64,
structural_remove_rate: f64,
min_genes: usize,
rng: &mut impl Rng,
) -> Vec<MutationOp> {
let log = mutate_with_log(chromosome, gene_pool, value_mutation_rate, rng);
structural_add_mutation(chromosome, gene_pool, structural_add_rate, rng);
structural_remove_mutation(chromosome, structural_remove_rate, min_genes, rng);
swap_mutation(chromosome, 0.1, rng);
scramble_mutation(chromosome, 0.05, rng);
log
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{SeedableRng, rngs::StdRng};
#[test]
fn empty_chromosome_mutation_is_noop() {
let mut chromosome = Chromosome::new(Vec::new());
let gene_pool = GenePool::default_wafrift();
let mut rng = StdRng::seed_from_u64(23);
let log = mutate_with_log(&mut chromosome, &gene_pool, 1.0, &mut rng);
assert!(chromosome.genes.is_empty());
assert!(log.is_empty());
}
}