use crate::archive::Archive;
use crate::fitness::Fitness;
use crate::genome::Genome;
use crate::population::{Individual, Population, PopulationConfig};
use crate::selection::Selection;
use crate::variation::{vary, VariationConfig};
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
#[derive(Debug, Clone)]
pub struct EvolutionConfig {
pub population: PopulationConfig,
pub variation: VariationConfig,
pub seed: Option<u64>,
}
impl Default for EvolutionConfig {
fn default() -> Self {
Self {
population: PopulationConfig::default(),
variation: VariationConfig::default(),
seed: None,
}
}
}
#[derive(Debug)]
pub struct EvolutionResult<G: Genome> {
pub population: Population<G>,
pub best: Individual<G>,
pub generations: usize,
}
pub struct Evolution<G, F, S>
where
G: Genome,
F: Fitness<G>,
S: Selection<G>,
{
population: Population<G>,
fitness: F,
selection: S,
archive: Option<Box<dyn Archive<G>>>,
config: EvolutionConfig,
rng: ChaCha8Rng,
}
impl<G, F, S> Evolution<G, F, S>
where
G: Genome,
F: Fitness<G>,
S: Selection<G>,
{
pub fn new(fitness: F, selection: S, config: EvolutionConfig) -> Self {
let seed = config.seed.unwrap_or_else(rand::random);
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let population = Population::random(config.population.clone(), &mut rng);
Self {
population,
fitness,
selection,
archive: None,
config,
rng,
}
}
pub fn with_archive(mut self, archive: Box<dyn Archive<G>>) -> Self {
self.archive = Some(archive);
self
}
pub fn run(&mut self, generations: usize) -> EvolutionResult<G> {
self.evaluate_population();
for _gen in 0..generations {
let offspring = self.create_offspring();
let evaluated = self.evaluate_batch(offspring);
if let Some(archive) = &mut self.archive {
for ind in &evaluated {
archive.add(ind.clone());
}
}
self.select_survivors(evaluated);
self.population.generation += 1;
}
let best = self.population.best().cloned().unwrap_or_else(|| {
Individual::new(G::random(&mut self.rng))
});
EvolutionResult {
population: self.population.clone(),
best,
generations,
}
}
fn evaluate_population(&mut self) {
let unevaluated: Vec<_> = self
.population
.individuals
.iter()
.enumerate()
.filter(|(_, ind)| ind.fitness.is_none())
.map(|(i, ind)| (i, ind.genome.clone()))
.collect();
if unevaluated.is_empty() {
return;
}
let genomes: Vec<_> = unevaluated.iter().map(|(_, g)| g.clone()).collect();
let results = self.fitness.evaluate_batch(&genomes);
for ((idx, _), result) in unevaluated.into_iter().zip(results) {
self.population.individuals[idx].fitness = Some(result.value);
self.population.individuals[idx].behavior = result.behavior;
}
}
fn evaluate_batch(&self, mut individuals: Vec<Individual<G>>) -> Vec<Individual<G>> {
if individuals.is_empty() {
return individuals;
}
let genomes: Vec<_> = individuals.iter().map(|ind| ind.genome.clone()).collect();
let results = self.fitness.evaluate_batch(&genomes);
for (ind, result) in individuals.iter_mut().zip(results) {
ind.fitness = Some(result.value);
ind.behavior = result.behavior;
}
individuals
}
fn create_offspring(&mut self) -> Vec<Individual<G>> {
let count = self.config.population.size - self.config.population.elitism;
(0..count)
.map(|_| {
let parent1 = self.selection.select(&self.population, &mut self.rng);
let parent2 = self.selection.select(&self.population, &mut self.rng);
let genome = vary(
&parent1.genome,
&parent2.genome,
&self.config.variation,
&mut self.rng,
);
Individual {
genome,
fitness: None,
behavior: None,
birth_generation: self.population.generation + 1,
}
})
.collect()
}
fn select_survivors(&mut self, offspring: Vec<Individual<G>>) {
let elites: Vec<_> = self
.population
.top_n(self.config.population.elitism)
.into_iter()
.cloned()
.collect();
let mut new_pop = elites;
new_pop.extend(offspring);
new_pop.truncate(self.config.population.size);
self.population.individuals = new_pop;
}
}
use rand_chacha;
use crate::archive::MapElitesArchive;
#[derive(Debug, Clone)]
pub struct MapElitesConfig {
pub batch_size: usize,
pub variation: VariationConfig,
pub seed: Option<u64>,
pub initial_population: usize,
}
impl Default for MapElitesConfig {
fn default() -> Self {
Self {
batch_size: 100,
variation: VariationConfig::default(),
seed: None,
initial_population: 100,
}
}
}
#[derive(Debug)]
pub struct MapElitesResult<G: Genome> {
pub archive: MapElitesArchive<G>,
pub best: Option<Individual<G>>,
pub generations: usize,
pub total_evaluations: usize,
}
pub struct MapElitesEvolution<G, F>
where
G: Genome,
F: Fitness<G>,
{
archive: MapElitesArchive<G>,
fitness: F,
config: MapElitesConfig,
rng: ChaCha8Rng,
total_evaluations: usize,
}
impl<G, F> MapElitesEvolution<G, F>
where
G: Genome,
F: Fitness<G>,
{
pub fn new(archive: MapElitesArchive<G>, fitness: F, config: MapElitesConfig) -> Self {
let seed = config.seed.unwrap_or_else(rand::random);
let rng = ChaCha8Rng::seed_from_u64(seed);
Self {
archive,
fitness,
config,
rng,
total_evaluations: 0,
}
}
pub fn initialize(&mut self) {
let genomes: Vec<G> = (0..self.config.initial_population)
.map(|_| G::random(&mut self.rng))
.collect();
let results = self.fitness.evaluate_batch(&genomes);
for (genome, result) in genomes.into_iter().zip(results) {
let mut individual = Individual::new(genome);
individual.fitness = Some(result.value);
individual.behavior = result.behavior;
self.archive.add(individual);
self.total_evaluations += 1;
}
}
pub fn run(&mut self, generations: usize) -> MapElitesResult<G> {
if self.archive.is_empty() {
self.initialize();
}
for _gen in 0..generations {
self.step();
}
let best = self.archive.best().cloned();
let dimensions = self.archive.dimensions().to_vec();
let archive = std::mem::replace(&mut self.archive, MapElitesArchive::new(dimensions));
MapElitesResult {
archive,
best,
generations,
total_evaluations: self.total_evaluations,
}
}
pub fn step(&mut self) {
let offspring = self.create_offspring();
let genomes: Vec<_> = offspring.iter().map(|ind| ind.genome.clone()).collect();
let results = self.fitness.evaluate_batch(&genomes);
for (mut ind, result) in offspring.into_iter().zip(results) {
ind.fitness = Some(result.value);
ind.behavior = result.behavior;
self.archive.add(ind);
self.total_evaluations += 1;
}
}
fn create_offspring(&mut self) -> Vec<Individual<G>> {
let mut offspring = Vec::with_capacity(self.config.batch_size);
for _ in 0..self.config.batch_size {
let child_genome = if self.archive.len() < 2 {
G::random(&mut self.rng)
} else {
let parent1 = self.archive.select_random(&mut self.rng);
let parent2 = self.archive.select_random(&mut self.rng);
match (parent1, parent2) {
(Some(p1), Some(p2)) => {
vary(
&p1.genome,
&p2.genome,
&self.config.variation,
&mut self.rng,
)
}
_ => G::random(&mut self.rng),
}
};
offspring.push(Individual::new(child_genome));
}
offspring
}
pub fn archive(&self) -> &MapElitesArchive<G> {
&self.archive
}
pub fn total_evaluations(&self) -> usize {
self.total_evaluations
}
pub fn coverage(&self) -> crate::archive::ArchiveCoverage {
self.archive.coverage()
}
}