From d1c17d01a6808bf6fcbe775110e213819cf819c8 Mon Sep 17 00:00:00 2001 From: logaritmisk Date: Wed, 3 Jan 2018 08:02:02 +0100 Subject: [PATCH] Added ParSimulation that is using rayon. --- Cargo.toml | 5 ++ benches/parabole.rs | 127 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 60 +++++++++++++++++++- src/lib.rs.bk | 131 -------------------------------------------- 4 files changed, 191 insertions(+), 132 deletions(-) create mode 100644 benches/parabole.rs delete mode 100644 src/lib.rs.bk diff --git a/Cargo.toml b/Cargo.toml index 64d6715..cb3a38c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,11 @@ path = "examples/parabole.rs" [dev-dependencies] rand = "0.3" +criterion = { git = "https://github.com/japaric/criterion.rs.git" } + +[[bench]] +name = "parabole" +harness = false [profile.release] lto = true diff --git a/benches/parabole.rs b/benches/parabole.rs new file mode 100644 index 0000000..d5b3b57 --- /dev/null +++ b/benches/parabole.rs @@ -0,0 +1,127 @@ +#[macro_use] +extern crate criterion; +extern crate rand; +extern crate rayon; +extern crate genetisk; + +use std::cmp::Ordering; + +use criterion::Criterion; +use rand::distributions::{IndependentSample, Range}; +use rayon::prelude::*; + +use genetisk::{Individual, Wrapper, Simulation}; + +#[derive(Clone, Copy, Debug)] +struct Fitness(f64); + +impl PartialEq for Fitness { + fn eq(&self, other: &Fitness) -> bool { + (self.0 - other.0).abs() < 0.0001 + } +} + +impl Eq for Fitness {} + +impl PartialOrd for Fitness { + fn partial_cmp(&self, other: &Fitness) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for Fitness { + fn cmp(&self, other: &Fitness) -> Ordering { + self.0.partial_cmp(&other.0).unwrap_or(Ordering::Equal) + } +} + + +#[derive(Clone, Debug)] +struct Parabole { + x: f64, +} + +impl Individual for Parabole { + type Fitness = Fitness; + + fn mate(&self, other: &Parabole) -> Parabole { + Parabole { x: (self.x + other.x) / 2.0 } + } + + fn mutate(&mut self) { + let between = Range::new(-1.0, 1.0); + + let mut rng = rand::thread_rng(); + let offset = between.ind_sample(&mut rng); + + self.x += offset; + } + + fn fitness(&self) -> Self::Fitness { + Fitness(10.0 - ((self.x + 3.0) * (self.x + 3.0))) + } +} + +fn criterion_benchmark(c: &mut Criterion) { + let initial = (0..1000) + .map(|i| Parabole { x: i as f64 }) + .collect::>(); + + let population_size = initial.len(); + + let mut simulation = Simulation::with_population(initial); + + c.bench_function("calculate", |b| b.iter(|| { + simulation.calculate(); + })); + + c.bench_function("evolve", |b| b.iter(|| { + simulation.evolve(|parents, population| { + // Mate top 10 to get 5 children. + parents + .iter() + .enumerate() + .filter(|&(n, _)| n % 2 == 0) + .map(|(_, wrapper)| wrapper) + .take(5) + .zip( + parents + .iter() + .enumerate() + .filter(|&(n, _)| n % 2 == 1) + .map(|(_, wrapper)| wrapper) + .take(5), + ) + .map(|(a, b)| a.individual.mate(&b.individual)) + .map(|individual| { + Wrapper { + individual: individual, + fitness: None, + } + }) + .for_each(|wrapper| { population.push(wrapper); }); + + // Mutate all to get new children. + parents + .par_iter() + .map(|wrapper| wrapper.individual.clone()) + .map(|mut individual| { + individual.mutate(); + + Wrapper { + individual: individual, + fitness: None, + } + }) + .collect_into(population); + + // Add all parents again. + population.extend(parents.iter().cloned()); + }); + + simulation.population.truncate(population_size); + })); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs index e09ccfa..87d3060 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,64 @@ where pub fn with_population(initial: Vec) -> Self { let mut population = Vec::with_capacity(initial.len() * 2); + initial + .into_iter() + .map(|individual| { + Wrapper { + individual: individual, + fitness: None, + } + }) + .for_each(|wrapper| { + population.push(wrapper); + }); + + Simulation { population: population } + } + + #[inline] + pub fn calculate(&mut self) { + self.population + .iter_mut() + .filter(|wrapper| wrapper.fitness.is_none()) + .for_each(|wrapper| { + wrapper.fitness = Some(wrapper.individual.fitness()) + }); + + self.population.sort_unstable_by( + |a, b| b.fitness.cmp(&a.fitness), + ); + } + + pub fn evolve(&mut self, func: F) + where + F: Fn(&[Wrapper], &mut Vec>), + { + let mut population = Vec::with_capacity(self.population.len()); + + func(&self.population, &mut population); + + self.population = population; + + self.calculate(); + } +} + + +pub struct ParSimulation +where + T: Individual, +{ + pub population: Vec>, +} + +impl ParSimulation +where + T: Individual, +{ + pub fn with_population(initial: Vec) -> Self { + let mut population = Vec::with_capacity(initial.len() * 2); + initial .into_par_iter() .map(|individual| { @@ -100,7 +158,7 @@ where }) .collect_into(&mut population); - Simulation { population: population } + ParSimulation { population: population } } #[inline] diff --git a/src/lib.rs.bk b/src/lib.rs.bk deleted file mode 100644 index 2f778e6..0000000 --- a/src/lib.rs.bk +++ /dev/null @@ -1,131 +0,0 @@ -extern crate rayon; - - -use rayon::prelude::*; - - -pub trait Individual: Send { - type Fitness: Send + Ord; - - fn fitness(&self) -> Self::Fitness; - - fn mate(&self, other: &Self) -> Self; - fn mutate(&mut self); -} - - -pub trait Select: Send -where - T: Individual -{ - fn select(&self, population: Vec>) -> Vec>; -} - - -pub struct MaximizeSelector { - count: usize, -} - -impl MaximizeSelector { - pub fn new(count: usize) -> Self { - MaximizeSelector { - count: count, - } - } -} - -impl Select for MaximizeSelector -where - T: Individual -{ - fn select(&self, mut population: Vec>) -> Vec> { - population.sort_unstable_by(|a, b| b.fitness.cmp(&a.fitness)); - - population.into_iter().take(self.count).collect() - } -} - - -pub struct MinimizeSelector { - count: usize, -} - -impl MinimizeSelector { - pub fn new(count: usize) -> Self { - MinimizeSelector { - count: count, - } - } -} - -impl Select for MinimizeSelector -where - T: Individual -{ - fn select(&self, mut population: Vec>) -> Vec> { - population.sort_unstable_by(|a, b| a.fitness.cmp(&b.fitness)); - - population.into_iter().take(self.count).collect() - } -} - - -#[derive(Clone)] -pub struct Wrapper -where - T: Individual, -{ - pub individual: T, - pub fitness: Option, -} - - -pub struct Simulation -where - T: Individual, -{ - pub population: Vec>, -} - -impl Simulation -where - T: Individual, -{ - pub fn with_population(initial: Vec) -> Self { - let mut population = Vec::with_capacity(initial.len() * 2); - - initial - .into_par_iter() - .map(|individual| Wrapper { - individual: individual, - fitness: None, - }) - .collect_into(&mut population); - - Simulation { - population: population, - } - } - - #[inline] - pub fn calculate(&mut self) { - self.population.par_iter_mut() - .filter(|wrapper| wrapper.fitness.is_none()) - .for_each(|wrapper| wrapper.fitness = Some(wrapper.individual.fitness())); - - self.population.par_sort_unstable_by(|a, b| b.fitness.cmp(&a.fitness)); - } - - pub fn evolve(&mut self, func: F) - where - F: Fn(&[Wrapper], &mut Vec>) - { - let mut population = Vec::with_capacity(self.population.len()); - - func(&self.population, &mut population); - - self.population = population; - - self.calculate(); - } -}