#![allow(clippy::print_stdout)]
use rand::{thread_rng, Rng};
use meiosis::algorithm::fitness::Configuration;
use meiosis::environment::Environment;
use meiosis::fitness::Fitness;
use meiosis::operators::mutation::combination::Combination;
use meiosis::operators::mutation::{add::Add, random::multi::nudge::Nudge, remove::Remove, swap::Swap};
use meiosis::operators::recombination::single_point::SinglePoint;
use meiosis::operators::selection::fitness_proportionate::StochasticAcceptance;
use meiosis::phenotype::{FromGenotype, Phenotype};
use meiosis::termination::fitness::Fitness as FitnessTermination;
const TARGET: &str = "Hello, World!";
#[derive(Debug, Clone, PartialEq)]
struct EvolvedString(Vec<u8>);
impl Phenotype for EvolvedString {
type Genotype = Vec<u8>;
fn to_genotype(self) -> Self::Genotype {
self.0
}
}
struct EvolveHelloWorld {
target: Vec<u8>,
}
impl FromGenotype<Vec<u8>, EvolveHelloWorld> for EvolvedString {
fn from_genotype<RNG>(_rng: &mut RNG, genotype: Vec<u8>, _environment: &EvolveHelloWorld) -> Self
where
RNG: Rng,
{
EvolvedString(genotype)
}
}
impl Environment for EvolveHelloWorld {}
impl Fitness<EvolveHelloWorld> for EvolvedString {
#[allow(clippy::cast_precision_loss, clippy::float_arithmetic)]
fn fitness(&self, environment: &EvolveHelloWorld) -> f64 {
let chars = &self.0;
let target = &environment.target;
let correct = chars
.iter()
.zip(target)
.map(|(c, t)| c == t)
.filter(|result| *result)
.count();
correct as f64 / self.0.len().max(environment.target.len()) as f64
}
}
fn main() {
let mutation = Combination::selective()
.and(Add {
chance: 1.,
max_genes: None,
})
.and(Remove {
chance: 1.,
min_genes: 5.try_into().unwrap(),
})
.and(Swap { chance: 1. })
.and(Nudge {
maximum_distance: 10.try_into().unwrap(),
rate: 0.3,
});
let config = Configuration::new()
.with_rng(thread_rng())
.with_environment(EvolveHelloWorld {
target: TARGET.as_bytes().to_vec(),
})
.with_selection(StochasticAcceptance::default())
.with_recombination(SinglePoint {})
.with_mutation(mutation)
.with_termination(FitnessTermination(1.));
let mut classic = config
.with_elitism(2)
.with_speciation_threshold(0.999)
.with_interspecies_breeding_chance(0.005)
.with_random_population(100);
let result = loop {
classic = match classic.evaluate::<EvolvedString>().check_termination() {
Ok(new_evaluated) => {
let iteration = new_evaluated.statistics().iteration;
if iteration % 100 == 0 {
let species = new_evaluated.state().species();
let population_count = species.iter().map(|s| s.population.members.len()).sum::<usize>();
println!(
"generation: {iteration}, population: {population_count}, species: {}",
species.len(),
);
for evaluated_species in species.iter() {
#[allow(clippy::expect_used)]
let fittest = evaluated_species
.population
.members
.iter()
.max_by(|a, b| a.fitness.total_cmp(&b.fitness))
.expect("iterator can not be empty");
println!(
"\tSpecies: {}-{}, members: {}, fitness: {}, best member: \"{}\"",
evaluated_species.identifier.0,
evaluated_species.identifier.1,
evaluated_species.population.members.len(),
fittest.fitness,
String::from_utf8_lossy(&fittest.phenotype.0),
);
}
}
new_evaluated.select().recombine().mutate().reinsert().into()
}
Err(terminated) => {
break terminated;
}
};
};
let species = result.state().species();
#[allow(clippy::expect_used)]
let fittest = species
.iter()
.flat_map(|s| &s.population.members)
.max_by(|a, b| a.fitness.total_cmp(&b.fitness))
.expect("iterator can not be empty");
println!(
"generation: {}, fitness: {}, best member: \"{}\"",
result.statistics().iteration,
fittest.fitness,
String::from_utf8_lossy(&fittest.phenotype.0),
);
}