use crate::test;
use crate::test::Vals;
use crate::Callable;
use crate::Crossover;
use crate::Mutate;
use crate::Objective;
use crate::StropError;
use crate::genetic::ScoredCandidate;
#[allow(dead_code)] #[derive(Debug)]
pub struct Optimize<
InputParameters,
ReturnValue,
T: Callable<InputParameters, ReturnValue>,
U: Callable<InputParameters, ReturnValue> + Mutate + Crossover,
Obj: Objective<U>,
> {
target_function: T,
population: Vec<ScoredCandidate<InputParameters, ReturnValue, U>>,
objective: Obj,
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,
Obj: Objective<U>,
> Optimize<InputParameters, ReturnValue, T, U, Obj>
{
pub fn new(target_function: T, objective: Obj) -> 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,
objective,
}
}
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::thread_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 = thread_rng().gen_range(0.0..total_fitness);
for i in 0..self.population.len() {
pick -= self.population[i].score;
if pick <= 0.0 {
next_generation.push(self.population.remove(i));
}
}
}
next_generation
}
pub fn push(&mut self, candidate: U) -> bool {
match test::passes(&candidate, &self.tests) {
Err(StropError::DidntReturn) => {
false
}
Err(StropError::Undefined) => {
false
}
Ok(false) => {
false
}
Ok(true) => {
if let Some(test_case) = test::fuzz(&self.target_function, &candidate, 5000) {
self.tests.push(test_case);
false
} else {
let score = self.objective.score(&candidate);
self.population
.push(ScoredCandidate::new_with_score(candidate, score));
true
}
}
}
}
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.push(U::crossover(parent_a.as_ref(), parent_b.as_ref()));
self.push(U::crossover(parent_b.as_ref(), parent_a.as_ref()));
}
fn mutate(&mut self, parent: &ScoredCandidate<InputParameters, ReturnValue, U>) {
let mut child = parent.candidate.clone();
child.mutate();
self.push(child);
}
pub fn next_generation(&mut self) {
use rand::Rng;
let mut rng = rand::thread_rng();
for _ in 0..((self.popsize as f64 * self.crossover_rate) as usize) {
let parent_a = rng.gen_range(0..self.population.len());
let parent_b = rng.gen_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.gen_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 next_specimen(&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
}
}