use super::transaction::{InsertStep, TransactionResult};
use super::{Graph, GraphChromosome};
use crate::node::Node;
use crate::{Arity, Factory, NodeType};
use radiate_core::{AlterContext, Chromosome};
use radiate_core::{AlterResult, Mutate, random_provider};
const INVALID_MUTATION: &str = "mutate.graph.invalid";
#[derive(Clone, Debug)]
pub struct GraphMutator {
vertex_rate: f32,
edge_rate: f32,
allow_recurrent: bool,
}
impl GraphMutator {
pub fn new(vertex_rate: f32, edge_rate: f32) -> Self {
GraphMutator {
vertex_rate,
edge_rate,
allow_recurrent: true,
}
}
pub fn allow_recurrent(mut self, allow: bool) -> Self {
self.allow_recurrent = allow;
self
}
fn mutate_type(&self) -> Option<NodeType> {
random_provider::with_rng(|rand| {
if rand.bool(0.5) {
if rand.bool(self.edge_rate) {
Some(NodeType::Edge)
} else {
None
}
} else if rand.bool(self.vertex_rate) {
Some(NodeType::Vertex)
} else {
None
}
})
}
}
impl<T> Mutate<GraphChromosome<T>> for GraphMutator
where
T: Clone + PartialEq + Default,
{
#[inline]
fn mutate_chromosome(
&self,
chromosome: &mut GraphChromosome<T>,
ctx: &mut AlterContext,
) -> AlterResult {
if let Some(max_nodes) = chromosome.max_nodes() {
if chromosome.len() >= max_nodes {
return AlterResult::empty();
}
}
if let Some(node_type) = self.mutate_type()
&& let Some(store) = chromosome.store()
{
let Some(new_node) = store.new_instance((chromosome.len(), node_type)) else {
ctx.metric(INVALID_MUTATION, 1);
return AlterResult::empty();
};
let mut graph = Graph::new(chromosome.take_nodes());
let result = random_provider::with_rng(|rand| {
graph.try_modify(|mut trans| {
let needed_insertions = match new_node.arity() {
Arity::Exact(n) => n,
_ => 1,
};
let target_idx = trans.random_target_node(rand).map(|n| n.index());
let source_idx = (0..needed_insertions)
.filter_map(|_| trans.random_source_node(rand).map(|n| n.index()))
.collect::<Vec<usize>>();
let node_idx = trans.push(new_node);
if let Some(trgt) = target_idx {
for src in source_idx {
let insertion_type =
trans.get_insertion_steps(src, trgt, node_idx, rand);
for step in insertion_type {
match step {
InsertStep::Connect(source, target) => {
trans.attach(source, target)
}
InsertStep::Detach(source, target) => {
trans.detach(source, target)
}
_ => {}
}
}
}
}
trans.commit_with(|graph: &Graph<T>| {
self.allow_recurrent || !graph.iter().any(|node| node.is_recurrent())
})
})
});
chromosome.set_nodes(graph.take_nodes());
return match result {
TransactionResult::Invalid(_, _) => {
ctx.metric(INVALID_MUTATION, 1);
AlterResult::empty()
}
TransactionResult::Valid(steps) => AlterResult::from(steps.len()),
};
}
AlterResult::empty()
}
}