use crate::populations::PopulationConfig;
use crate::Genome;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct SpeciesID(pub usize, pub usize);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Species<G> {
id: SpeciesID,
pub(super) genomes: Vec<G>,
representative: G,
stagnation: usize,
max_fitness: f32,
}
impl<G: Genome + Clone> Species<G> {
pub fn new(id: SpeciesID, representative: G) -> Species<G> {
Species {
id,
genomes: vec![representative.clone()],
representative,
stagnation: 0,
max_fitness: 0.0,
}
}
pub fn id(&self) -> SpeciesID {
self.id
}
pub fn representative(&self) -> &G {
&self.representative
}
pub fn add_genome(&mut self, genome: G) {
self.genomes.push(genome);
}
pub(super) fn update_fitness(&mut self) {
let max_fitness = self
.genomes
.iter()
.map(|g| g.fitness())
.max_by(|a, b| {
a.partial_cmp(b)
.unwrap_or_else(|| panic!("uncomparable fitness value detected"))
})
.unwrap_or(0.0);
if max_fitness <= self.max_fitness {
self.stagnation += 1;
}
self.max_fitness = max_fitness;
}
pub fn adjusted_fitness(&self) -> f32 {
self.genomes.iter().map(|g| g.fitness()).sum::<f32>() / self.genomes.len() as f32
}
pub fn time_stagnated(&self) -> usize {
self.stagnation
}
pub fn genomes(&self) -> impl Iterator<Item = &G> {
self.genomes.iter()
}
pub fn champion(&self) -> &G {
self.genomes
.iter()
.max_by(|g1, g2| {
g1.fitness()
.partial_cmp(&g2.fitness())
.unwrap_or_else(|| panic!("uncomparable fitness value detected"))
})
.expect("empty species has no champion")
}
pub(super) fn count_elite(&self, config: &PopulationConfig) -> usize {
self.genomes.len().min(config.elitism)
}
pub(super) fn count_survivors(&self, config: &PopulationConfig) -> usize {
(self.genomes.len() as f32 * config.survival_threshold).ceil() as usize
}
}
#[cfg(test)]
mod tests {}