use std::{collections::HashMap, mem};
use rand::seq::{IndexedRandom, SliceRandom};
use crate::{genome::{Genome, GlobalInnovator}, phenotype::Phenotype, species::{Species, SpeciesCounter}};
pub struct Population {
generation_number: usize,
innovator: GlobalInnovator,
species_counter: SpeciesCounter,
index_cache: HashMap<usize, (usize, usize)>,
pub species: Vec<Species>,
pub population_size: usize,
pub num_inputs: usize,
pub num_outputs: usize,
}
impl Population {
pub fn new(num_inputs: usize, num_outputs: usize, population_size: usize) -> Self {
let mut innovator = GlobalInnovator::new();
let mut species_counter = SpeciesCounter::new();
let population = (0..population_size).into_iter()
.map(|_| Genome::new(num_inputs, num_outputs))
.collect::<Vec<Genome>>();
let mut innovations = HashMap::new(); let mutated_population = population.into_iter()
.map(|genome| Phenotype::from_mutation(&genome, &mut innovator, &mut innovations))
.collect::<Vec<Phenotype>>();
let mut rng = rand::rng();
let chosen = mutated_population.choose(&mut rng).unwrap(); let mut species = vec![Species::new(&chosen.genome, species_counter.next())];
Species::sort_species(&mut species, mutated_population, &mut species_counter);
let mut population = Population {
generation_number: 0,
innovator,
species_counter,
population_size,
species,
num_inputs,
num_outputs,
index_cache: HashMap::with_capacity(population_size),
};
population.update_cache();
population
}
pub fn update_cache(&mut self) {
let mut population_counter = 0;
self.index_cache.clear();
for (i, s) in self.species.iter().enumerate() {
self.index_cache.extend((0..s.members.len()).into_iter()
.map(|x| (population_counter+x, (i, x))) );
population_counter += s.members.len();
}
assert_eq!(population_counter, 1000, "Population size mismatch discovered during caching!");
}
pub fn activate_index(&self, idx: usize, input: &mut Vec<f64>) -> Option<Vec<f64>> {
let pair = self.index_cache.get(&idx)?; let phenotype = &self.species[pair.0].members[pair.1];
Some(phenotype.activate(input))
}
pub fn distribute_evenly(total: usize, buckets: usize) -> Vec<usize> {
let mut result = vec![total / buckets; buckets];
let remainder = total % buckets;
let mut rng = rand::rng();
let mut indices: Vec<_> = (0..buckets).collect();
indices.shuffle(&mut rng);
for &i in indices.iter().take(remainder) {
result[i] += 1;
}
result
}
pub fn evolve(&mut self, fitnesses: &Vec<f64>) {
let fitness_by_species_index = fitnesses.iter()
.enumerate()
.map(|(i, x)| (*self.index_cache.get(&i).unwrap(), *x))
.collect::<Vec<((usize, usize), f64)>>();
let mut fitness_by_species = vec![];
for ((s_i, _), fitness) in fitness_by_species_index {
if s_i == fitness_by_species.len() {
fitness_by_species.push(vec![]);
fitness_by_species.last_mut().unwrap().push(fitness);
} else if s_i < fitness_by_species.len() {
fitness_by_species[s_i].push(fitness);
} else {
unreachable!(); }
}
let mut total_fitness = 0.0;
for (spec, fits) in self.species.iter_mut().zip(fitness_by_species) {
let mut zipped: Vec<_> = spec.members.drain(..).zip(fits).collect();
zipped.sort_by(|x, y| y.1.partial_cmp(&x.1).unwrap_or(std::cmp::Ordering::Less));
let (phens, fits): (Vec<_>, Vec<_>) = zipped.into_iter().unzip();
spec.members = phens;
spec.species_fitness = Some(fits.iter().sum::<f64>() / fits.len() as f64);
total_fitness += spec.species_fitness.unwrap(); }
let mut reproductive_slots: Vec<_> = self.species.iter()
.map(|s| (s.species_fitness.unwrap() / total_fitness * self.population_size as f64) as usize) .collect();
let total_slots: usize = reproductive_slots.iter().sum();
let remainder = self.population_size - total_slots;
if remainder > 0 {
let extra_slots = Population::distribute_evenly(remainder, self.species.len());
for (slots, species_slots) in extra_slots.into_iter().zip(&mut reproductive_slots) {
*species_slots += slots;
}
}
let species = mem::take(&mut self.species); let (reproductive_slots, mut species): (Vec<_>, Vec<_>) = reproductive_slots.into_iter()
.zip(species)
.filter(|(slots, _)| *slots != 0 as usize)
.collect();
let mut new_population = vec![];
let mut new_innovations: HashMap<(usize, usize), usize> = HashMap::new(); for (spec, slots) in species.iter_mut().zip(reproductive_slots) {
spec.species_fitness = None; spec.members.truncate(spec.members.len() / 2); spec.choose_type_specimen();
spec.populate(&mut new_population, slots, &mut self.innovator, &mut new_innovations);
}
Species::sort_species(&mut species, new_population, &mut self.species_counter);
self.update_cache();
self.generation_number += 1;
}
}