use super::{Chromosome, Phenotype, Population};
use crate::{Objective, Score, objectives::Scored, tracker::Tracker};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{
fmt::{self, Debug, Formatter},
sync::atomic::{AtomicU64, Ordering},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(transparent)]
pub struct SpeciesId(pub u64);
impl SpeciesId {
pub fn new() -> Self {
static SPECIES_ID: AtomicU64 = AtomicU64::new(0);
SpeciesId(SPECIES_ID.fetch_add(1, Ordering::Relaxed))
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SpeciesSnapshot {
pub id: SpeciesId,
pub generation: usize,
pub score: Option<Score>,
pub best_score: Option<Score>,
pub stagnation: usize,
pub population_size: usize,
}
impl<C: Chromosome> From<&Species<C>> for SpeciesSnapshot {
fn from(species: &Species<C>) -> Self {
SpeciesSnapshot {
id: species.id(),
generation: species.generation(),
score: species.score().cloned(),
best_score: species.tracker.current().cloned(),
stagnation: species.stagnation(),
population_size: species.len(),
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Species<C: Chromosome> {
pub id: SpeciesId,
pub generation: usize,
pub tracker: Tracker<Score>,
pub score: Option<Score>,
pub mascot: Phenotype<C>,
pub population: Population<C>,
}
impl<C: Chromosome> Species<C> {
pub fn new(generation: usize, initial: &Phenotype<C>) -> Self
where
C: Clone,
{
Species {
id: SpeciesId::new(),
generation,
tracker: Tracker::new(),
score: Some(initial.score().unwrap().clone()),
mascot: initial.clone(),
population: Population::new(vec![initial.clone()]),
}
}
pub fn id(&self) -> SpeciesId {
self.id
}
pub fn push(&mut self, individual: Phenotype<C>) {
self.population.push(individual);
}
pub fn population(&self) -> &Population<C> {
&self.population
}
pub fn stagnation(&self) -> usize {
self.tracker.stagnation()
}
pub fn len(&self) -> usize {
self.population.len()
}
pub fn set_new_mascot(&mut self, mascot: Phenotype<C>) {
self.mascot = mascot;
self.population.clear();
}
pub fn mascot(&self) -> &Phenotype<C> {
&self.mascot
}
pub fn score(&self) -> Option<&Score> {
self.score.as_ref()
}
pub fn age(&self, current: usize) -> usize {
current - self.generation
}
pub fn generation(&self) -> usize {
self.generation
}
pub fn update_score(&mut self, score: Score, objective: &Objective)
where
C: PartialEq,
{
objective.sort(&mut self.population);
self.score = Some(score);
self.tracker
.update(self.population[0].score().unwrap(), objective);
}
}
impl<C: Chromosome> Scored for Species<C> {
fn score(&self) -> Option<&Score> {
self.score.as_ref()
}
}
impl<C: Chromosome + Clone> Clone for Species<C> {
fn clone(&self) -> Self {
Species {
id: self.id,
generation: self.generation,
tracker: self.tracker.clone(),
score: self.score.clone(),
mascot: self.mascot.clone(),
population: self.population.clone(),
}
}
}
impl<C: Chromosome + PartialEq> PartialOrd for Species<C> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.score.as_ref().partial_cmp(&other.score.as_ref())
}
}
impl<C: Chromosome + PartialEq> PartialEq for Species<C> {
fn eq(&self, other: &Self) -> bool {
self.score() == other.score()
&& self.id == other.id
&& self.mascot() == other.mascot()
&& self.len() == other.len()
&& self.stagnation() == other.stagnation()
&& self.generation == other.generation
}
}
impl<C: Chromosome> Debug for Species<C> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"Species {{ members: {:?}, score: {:?}, best_score: {:?}, stagnation: {:?}, generation: {:?}, id: {:?} }}",
self.len(),
self.score,
self.tracker.current(),
self.tracker.stagnation(),
self.generation,
self.id
)
}
}