use super::chromosome::GpChromosome;
use super::node::{check_limits, grow_tree, GpNode, Node};
use crate::error::GaError;
use rand::Rng;
#[derive(Clone, Debug)]
pub enum GpMutation {
SubtreeMutation {
mutation_max_depth: usize,
},
PointMutation {
p_per_node: f64,
},
HoistMutation,
}
impl GpMutation {
pub fn apply<N: GpNode + Clone>(
&self,
chromosome: &mut GpChromosome<N>,
max_depth: usize,
max_node_count: usize,
rng: &mut impl Rng,
) -> Result<(), GaError> {
match self {
GpMutation::SubtreeMutation { mutation_max_depth } => subtree_mutation(
chromosome,
max_depth,
max_node_count,
*mutation_max_depth,
rng,
),
GpMutation::PointMutation { p_per_node } => {
point_mutation(chromosome, *p_per_node, rng);
Ok(())
}
GpMutation::HoistMutation => {
hoist_mutation(chromosome, rng);
Ok(())
}
}
}
}
fn subtree_mutation<N: GpNode + Clone>(
chromosome: &mut GpChromosome<N>,
max_depth: usize,
max_node_count: usize,
mutation_max_depth: usize,
rng: &mut impl Rng,
) -> Result<(), GaError> {
let n = chromosome.root.node_count();
let target = rng.random_range(0..n);
let new_subtree = grow_tree::<N>(mutation_max_depth, rng);
let mut candidate = chromosome.root.clone();
replace_node_in_place(&mut candidate, target, Box::new(new_subtree));
check_limits(&candidate, max_depth, max_node_count)?;
chromosome.root = candidate;
Ok(())
}
fn point_mutation<N: GpNode + Clone>(
chromosome: &mut GpChromosome<N>,
p_per_node: f64,
rng: &mut impl Rng,
) {
point_mutation_node(&mut chromosome.root, p_per_node, rng);
}
fn point_mutation_node<N: GpNode + Clone>(
node: &mut Box<Node<N>>,
p_per_node: f64,
rng: &mut impl Rng,
) {
if rng.random::<f64>() < p_per_node {
match node.as_mut() {
Node::Terminal(v) => {
*v = N::sample_random_terminal(rng);
}
Node::Function { value, .. } => {
let arity = value.arity();
let alternatives: Vec<N> = N::all_functions()
.into_iter()
.filter(|f| f.arity() == arity)
.collect();
if !alternatives.is_empty() {
let idx = rng.random_range(0..alternatives.len());
*value = alternatives[idx].clone();
}
}
}
}
if let Node::Function { children, .. } = node.as_mut() {
for child in children.iter_mut() {
point_mutation_node(child, p_per_node, rng);
}
}
}
fn hoist_mutation<N: GpNode + Clone>(chromosome: &mut GpChromosome<N>, rng: &mut impl Rng) {
let mut function_indices: Vec<usize> = Vec::new();
collect_function_indices(&chromosome.root, &mut function_indices, &mut 0);
if function_indices.is_empty() {
return;
}
let s1_idx_in_list = rng.random_range(0..function_indices.len());
let s1_preorder = function_indices[s1_idx_in_list];
let s1_node =
clone_subtree_at_index_local(&chromosome.root, s1_preorder).expect("s1_preorder is valid");
let s1_size = s1_node.node_count();
let s2_local_idx = if s1_size > 1 {
rng.random_range(1..s1_size)
} else {
0
};
let s2_node = clone_subtree_at_index_local(&s1_node, s2_local_idx)
.expect("s2_local_idx is valid within s1_node");
replace_node_in_place(&mut chromosome.root, s1_preorder, Box::new(s2_node));
}
fn collect_function_indices<N: GpNode>(
node: &Node<N>,
indices: &mut Vec<usize>,
current_index: &mut usize,
) {
let my_index = *current_index;
*current_index += 1;
match node {
Node::Terminal(_) => {}
Node::Function { children, .. } => {
indices.push(my_index);
for child in children {
collect_function_indices(child, indices, current_index);
}
}
}
}
fn clone_subtree_at_index_local<N: GpNode + Clone>(
root: &Node<N>,
target: usize,
) -> Option<Node<N>> {
let mut idx = 0usize;
clone_at_idx(root, target, &mut idx)
}
fn clone_at_idx<N: GpNode + Clone>(
node: &Node<N>,
target: usize,
current: &mut usize,
) -> Option<Node<N>> {
let my_index = *current;
*current += 1;
if my_index == target {
return Some(node.clone());
}
match node {
Node::Terminal(_) => None,
Node::Function { children, .. } => {
for child in children {
if let Some(found) = clone_at_idx(child, target, current) {
return Some(found);
}
}
None
}
}
}
fn replace_node_in_place<N: GpNode>(
root: &mut Box<Node<N>>,
target: usize,
replacement: Box<Node<N>>,
) {
let mut replacer = NodeReplacer::new(target, replacement);
let mut idx = 0usize;
replacer.run(root, &mut idx);
}
struct NodeReplacer<N: GpNode> {
target: usize,
replacement: Option<Box<Node<N>>>,
}
impl<N: GpNode> NodeReplacer<N> {
fn new(target: usize, replacement: Box<Node<N>>) -> Self {
NodeReplacer {
target,
replacement: Some(replacement),
}
}
fn run(&mut self, node: &mut Box<Node<N>>, current_index: &mut usize) {
if self.replacement.is_none() {
return;
}
let my_index = *current_index;
*current_index += 1;
if my_index == self.target {
let replacement = self.replacement.take().unwrap();
*node = replacement;
return;
}
if let Node::Function { children, .. } = node.as_mut() {
for child in children.iter_mut() {
self.run(child, current_index);
if self.replacement.is_none() {
return;
}
}
}
}
}