use crate::test::Vals;
use crate::Callable;
use crate::Crossover;
use crate::Mutate;
use crate::genetic::ScoredCandidate;
#[allow(dead_code)] #[derive(Debug)]
pub struct Generate<
InputParameters,
ReturnValue,
T: Callable<InputParameters, ReturnValue>,
U: Callable<InputParameters, ReturnValue> + Mutate + Crossover,
> {
target_function: T,
population: Vec<ScoredCandidate<InputParameters, ReturnValue, U>>,
tests: Vec<(InputParameters, ReturnValue)>,
popsize: usize,
input: std::marker::PhantomData<InputParameters>,
ret: std::marker::PhantomData<ReturnValue>,
crossover_rate: f64,
mutation_rate: f64,
}
impl<
InputParameters: Vals,
ReturnValue: Vals,
T: Callable<InputParameters, ReturnValue>,
U: Callable<InputParameters, ReturnValue> + Mutate + Crossover + Clone + crate::Disassemble,
> Generate<InputParameters, ReturnValue, T, U>
{
pub fn new(target_function: T) -> Self {
Self {
tests: crate::test::quick_tests(&target_function),
target_function,
population: vec![],
popsize: 100,
input: Default::default(),
ret: Default::default(),
crossover_rate: 2.0,
mutation_rate: 1.0,
}
}
pub fn population_size(&mut self, population_size: usize) {
self.popsize = population_size;
}
pub fn crossover_rate(&mut self, crossover_rate: f64) {
self.crossover_rate = crossover_rate;
}
pub fn mutation_rate(&mut self, mutation_rate: f64) {
self.mutation_rate = mutation_rate;
}
fn roulette_selection(&mut self) -> Vec<ScoredCandidate<InputParameters, ReturnValue, U>> {
use rand::rng;
use rand::Rng;
let mut next_generation = vec![];
for _ in 0..self.popsize {
let total_fitness: f64 = self.population.iter().map(|ind| ind.score).sum();
let mut pick = rng().random_range(0.0..total_fitness);
for i in 0..(self.population.len() - 1) {
pick -= self.population[i].score;
if pick <= 0.0 {
next_generation.push(self.population.remove(i));
break;
}
}
}
next_generation
}
pub fn selection(&mut self) {
let next_generation = self.roulette_selection();
self.population = next_generation;
}
fn crossover(
&mut self,
parent_a: &ScoredCandidate<InputParameters, ReturnValue, U>,
parent_b: &ScoredCandidate<InputParameters, ReturnValue, U>,
) {
self.population.push(ScoredCandidate::new(
U::crossover(parent_a.as_ref(), parent_b.as_ref()),
&self.tests,
));
self.population.push(ScoredCandidate::new(
U::crossover(parent_b.as_ref(), parent_a.as_ref()),
&self.tests,
));
}
fn mutate(&mut self, parent: &ScoredCandidate<InputParameters, ReturnValue, U>) {
let mut child = parent.candidate.clone();
child.mutate();
self.population
.push(ScoredCandidate::new(child, &self.tests));
}
pub fn next_generation(&mut self) {
use rand::Rng;
let mut rng = rand::rng();
if self.population.is_empty() {
self.population
.push(ScoredCandidate::new(U::random(), &self.tests));
}
let total_fitness: f64 = self.population.iter().map(|ind| ind.score).sum();
println!(
"population size: {} total_fitness: {}",
self.population.len(),
total_fitness
);
if self.population.len() == 1 {
self.population[0].candidate.dasm();
}
for _ in 0..((self.popsize as f64 * self.crossover_rate) as usize) {
let parent_a = rng.random_range(0..self.population.len());
let parent_b = rng.random_range(0..self.population.len());
let parent_a = self.population[parent_a].clone();
let parent_b = self.population[parent_b].clone();
self.crossover(&parent_a, &parent_b);
}
for _ in 0..((self.popsize as f64 * self.mutation_rate) as usize) {
let parent = rng.random_range(0..self.population.len());
let parent = &self.population[parent].clone();
self.mutate(parent);
}
self.selection();
}
fn offset_to_first_zero(&self) -> Option<usize> {
self.population.iter().position(|x| x.score == 0.0)
}
pub fn search(&mut self) -> U {
while self.offset_to_first_zero().is_none() {
self.next_generation();
}
let specimen = self.population.remove(self.offset_to_first_zero().unwrap());
specimen.candidate
}
}