use std::{
cmp::Ordering,
time::{Duration, Instant},
};
use rand::{Rng, seq::SliceRandom};
use crate::genetic::{
chromosome::{Chromosome, Gene},
error::GeneticError,
fitness::Fitness,
offspring::Offspring,
};
#[derive(Clone)]
pub struct Individual<C>
where
C: Chromosome,
{
chromosome: C,
}
enum MateResult {
Parent1,
Parent2,
Mutation,
}
impl<C> Individual<C>
where
C: Chromosome,
{
#[inline]
pub fn chromosome(&self) -> &C {
&self.chromosome
}
#[inline]
pub fn is_optimal(&self) -> bool {
self.chromosome.is_optimal()
}
pub fn mate(
&self,
rng: &mut impl Rng,
partner: &Individual<C>,
mutation_probability: f64,
maybe_max_runtime: Option<&Duration>,
) -> Result<Offspring<C>, GeneticError> {
let time = Instant::now();
let mut mutations = 0u64;
let mut child_chromosome = self.chromosome.base();
let mut child_genes = vec![None; self.chromosome.len()];
loop {
if let Some(max_runtime) = maybe_max_runtime
&& time.elapsed().ge(max_runtime)
{
return Err(GeneticError::MateTimeout);
}
let mut gene_indexes = (0..self.chromosome.len()).collect::<Vec<_>>();
gene_indexes.shuffle(rng);
for index in gene_indexes {
let gene = match get_mate_result(rng, mutation_probability) {
MateResult::Parent1 => {
let mut gene = self.chromosome.get(index).clone();
gene.modify(&child_genes);
gene
},
MateResult::Parent2 => {
let mut gene = partner.chromosome.get(index).clone();
gene.modify(&child_genes);
gene
},
MateResult::Mutation => {
mutations += 1;
let mut gene = self.chromosome.get(index).clone();
gene.mutate(rng, &child_genes);
gene
},
};
child_genes[index] = Some(gene);
}
for gene in child_genes.iter_mut() {
let gene = gene.take().ok_or(GeneticError::Internal)?;
child_chromosome.push(gene);
}
if child_chromosome.len() != self.chromosome.len() {
return Err(GeneticError::Internal);
}
if child_chromosome.is_valid() {
break;
}
child_chromosome.clear();
child_genes.clear();
child_genes.resize(self.chromosome.len(), None);
}
let offspring = Offspring::new(child_chromosome.into(), mutations);
Ok(offspring)
}
}
impl<C> From<C> for Individual<C>
where
C: Chromosome,
{
fn from(chromosome: C) -> Self {
Individual {
chromosome,
}
}
}
impl<C> Ord for Individual<C>
where
C: Chromosome,
{
fn cmp(&self, other: &Self) -> Ordering {
match self.chromosome.fitness_cmp(other.chromosome()) {
Fitness::Stronger => Ordering::Less,
Fitness::Weaker => Ordering::Greater,
Fitness::Equal => Ordering::Equal,
}
}
}
impl<C> PartialOrd for Individual<C>
where
C: Chromosome,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<C> PartialEq for Individual<C>
where
C: Chromosome,
{
fn eq(&self, other: &Self) -> bool {
matches!(
self.chromosome.fitness_cmp(other.chromosome()),
Fitness::Equal
)
}
}
impl<C> Eq for Individual<C> where C: Chromosome {}
fn get_mate_result(rng: &mut impl Rng, mutation_probability: f64) -> MateResult {
let random: f64 = rng.random();
if random < (1.0 - mutation_probability) / 2.0 {
return MateResult::Parent1;
}
if random < 1.0 - mutation_probability {
return MateResult::Parent2;
}
MateResult::Mutation
}