use super::chromosome::GpChromosome;
use super::node::{check_limits, GpNode, Node};
use crate::error::GaError;
use rand::Rng;
#[derive(Clone, Debug)]
pub enum GpCrossover {
SubtreeCrossover,
}
impl GpCrossover {
pub fn apply<N: GpNode + Clone>(
&self,
p1: &GpChromosome<N>,
p2: &GpChromosome<N>,
max_depth: usize,
max_node_count: usize,
rng: &mut impl Rng,
) -> Result<(GpChromosome<N>, GpChromosome<N>), GaError> {
match self {
GpCrossover::SubtreeCrossover => {
subtree_crossover(p1, p2, max_depth, max_node_count, rng)
}
}
}
}
fn clone_subtree_at_index<N: GpNode + Clone>(root: &Node<N>, target: usize) -> Option<Node<N>> {
let mut current_index = 0usize;
clone_at_index_impl(root, target, &mut current_index)
}
fn clone_at_index_impl<N: GpNode + Clone>(
node: &Node<N>,
target: usize,
current_index: &mut usize,
) -> Option<Node<N>> {
let my_index = *current_index;
*current_index += 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_index_impl(child, target, current_index) {
return Some(found);
}
}
None
}
}
}
struct Replacer<N: GpNode> {
target: usize,
replacement: Option<Box<Node<N>>>,
}
impl<N: GpNode> Replacer<N> {
fn new(target: usize, replacement: Box<Node<N>>) -> Self {
Replacer {
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;
}
}
}
}
}
fn subtree_crossover<N: GpNode + Clone>(
p1: &GpChromosome<N>,
p2: &GpChromosome<N>,
max_depth: usize,
max_node_count: usize,
rng: &mut impl Rng,
) -> Result<(GpChromosome<N>, GpChromosome<N>), GaError> {
let n1 = p1.root.node_count();
let n2 = p2.root.node_count();
let i1 = rng.random_range(0..n1);
let i2 = rng.random_range(0..n2);
let mut child1_root = p1.root.clone();
let mut child2_root = p2.root.clone();
let sub1 = clone_subtree_at_index(&p1.root, i1).expect("i1 is within p1.root bounds");
let sub2 = clone_subtree_at_index(&p2.root, i2).expect("i2 is within p2.root bounds");
{
let mut replacer = Replacer::new(i1, Box::new(sub2));
let mut idx = 0usize;
replacer.run(&mut child1_root, &mut idx);
}
{
let mut replacer = Replacer::new(i2, Box::new(sub1));
let mut idx = 0usize;
replacer.run(&mut child2_root, &mut idx);
}
check_limits(&child1_root, max_depth, max_node_count)?;
check_limits(&child2_root, max_depth, max_node_count)?;
Ok((
GpChromosome::with_root(child1_root),
GpChromosome::with_root(child2_root),
))
}