radiate-core 1.3.0

Core traits and interfaces for the Radiate genetic algorithm library.
Documentation
use super::{Chromosome, Phenotype};
use crate::{
    Objective, Score, objectives::Scored, phenotype::PhenotypeId, sentry_id, tracker::Tracker,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{
    collections::HashSet,
    fmt::{self, Debug, Formatter},
    sync::atomic::AtomicU64,
};

sentry_id!(SpeciesId);

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Species<C: Chromosome> {
    pub id: SpeciesId,
    pub generation: usize,
    pub tracker: Tracker<Score>,
    pub adjusted_score: Option<Score>,
    pub mascot: Phenotype<C>,
    pub size: usize,
    pub members: HashSet<PhenotypeId>,
}

impl<C: Chromosome> Species<C> {
    pub fn new(generation: usize, initial: Phenotype<C>) -> Self {
        Species {
            id: SpeciesId::new(),
            generation,
            tracker: Tracker::new(),
            adjusted_score: Some(initial.score().unwrap().clone()),
            mascot: initial,
            size: 0,
            members: HashSet::new(),
        }
    }

    pub fn id(&self) -> SpeciesId {
        self.id
    }

    pub fn add_member(&mut self, phenotype_id: PhenotypeId) {
        self.size += 1;
        self.members.insert(phenotype_id);
    }

    pub fn stagnation(&self) -> usize {
        self.tracker.stagnation()
    }

    pub fn len(&self) -> usize {
        self.size
    }

    pub fn is_empty(&self) -> bool {
        self.size == 0
    }

    pub fn set_new_mascot(&mut self, mascot: Phenotype<C>) {
        self.mascot = mascot;
        self.size = 0;
        self.members.clear();
    }

    pub fn mascot(&self) -> &Phenotype<C> {
        &self.mascot
    }

    pub fn adj_score(&self) -> Option<&Score> {
        self.adjusted_score.as_ref()
    }

    pub fn raw_score(&self) -> Option<&Score> {
        self.tracker.best()
    }

    pub fn age(&self, current: usize) -> usize {
        current - self.generation
    }

    pub fn generation(&self) -> usize {
        self.generation
    }

    pub fn update_score(&mut self, raw_score: Score, adjusted_score: Score, objective: &Objective)
    where
        C: PartialEq,
    {
        self.adjusted_score = Some(adjusted_score);
        self.tracker.update(&raw_score, objective);
    }
}

impl<C: Chromosome> Scored for Species<C> {
    fn score(&self) -> Option<&Score> {
        self.adjusted_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(),
            adjusted_score: self.adjusted_score.clone(),
            mascot: self.mascot.clone(),
            size: self.size,
            members: self.members.clone(),
        }
    }
}

impl<C: Chromosome + PartialEq> PartialOrd for Species<C> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.adjusted_score
            .as_ref()
            .partial_cmp(&other.adjusted_score.as_ref())
    }
}
impl<C: Chromosome + PartialEq> PartialEq for Species<C> {
    fn eq(&self, other: &Self) -> bool {
        self.adj_score() == other.adj_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,
            "id={}  size={:>2}  adj={}  raw={}  stag={}  gen={}",
            self.id.get(),
            self.len(),
            fmt_opt_score(self.adjusted_score.as_ref()),
            fmt_opt_score(self.tracker.current()),
            self.tracker.stagnation(),
            self.generation,
        )
    }
}

fn fmt_opt_score(score: Option<&Score>) -> String {
    match score {
        Some(s) if s.is_single_objective() => format!("{:.6}", s.first().unwrap_or(f32::NAN)),
        Some(s) => format!("{:?}", s.as_slice()),
        None => "?".to_string(),
    }
}