Did things

This commit is contained in:
2021-10-26 21:28:18 +02:00
parent e77ce84ca1
commit 37fcb3b438
4 changed files with 110 additions and 418 deletions

View File

@@ -5,16 +5,7 @@ authors = ["logaritmisk <anders.e.olsson@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
rayon = "1.0"
[[example]]
name = "parabole"
path = "examples/parabole.rs"
[dev-dependencies]
criterion = "0.2"
rand = "0.6" rand = "0.6"
[[bench]] [dev-dependencies]
name = "parabole" approx = "0.3"
harness = false

View File

@@ -1,138 +0,0 @@
#[macro_use]
extern crate criterion;
extern crate genetisk;
extern crate rand;
extern crate rayon;
use std::cmp::Ordering;
use criterion::Criterion;
use rand::distributions::{IndependentSample, Range};
use rayon::prelude::*;
use genetisk::{Individual, Simulation, Wrapper};
#[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<Ordering> {
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 mut rng = rand::weak_rng();
let between = Range::new(-1.0, 1.0);
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) {
c.bench_function("calculate", move |b| {
let initial = (0..1000)
.map(|i| Parabole { x: f64::from(i) })
.collect::<Vec<_>>();
let mut simulation = Simulation::with_population(initial);
b.iter(|| {
simulation.calculate();
});
});
c.bench_function("evolve", move |b| {
let initial = (0..1000)
.map(|i| Parabole { x: f64::from(i) })
.collect::<Vec<_>>();
let population_size = initial.len();
let mut simulation = Simulation::with_population(initial);
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,
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,
fitness: None,
}
})
.collect_into_vec(population);
// Add all parents again.
population.extend(parents.iter().cloned());
});
simulation.population.truncate(population_size);
});
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@@ -1,126 +0,0 @@
use std::cmp::Ordering;
use genetisk::{Individual, Simulation, Wrapper};
use rand::distributions::{IndependentSample, Range};
use rayon::prelude::*;
#[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<Ordering> {
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 mut rng = rand::weak_rng();
let between = Range::new(-1.0, 1.0);
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 main() {
let initial = (0..300)
.map(|i| Parabole { x: i as f64 })
.collect::<Vec<_>>();
let population_size = initial.len();
let mut simulation = Simulation::with_population(initial);
simulation.calculate();
for _ in 0..250_000 {
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,
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,
fitness: None,
}
})
.collect_into_vec(population);
// Add all parents again.
population.extend(parents.iter().cloned());
});
simulation.population.truncate(population_size);
}
println!(
"{:#?}",
simulation
.population
.iter()
.take(10)
.map(|individual| &individual.individual)
.collect::<Vec<_>>()
);
println!();
}

View File

@@ -1,170 +1,135 @@
use rayon::prelude::*; // https://en.wikipedia.org/wiki/Test_functions_for_optimization
pub trait Individual: Send { /*
type Fitness: Send + Ord; pub trait Genotype {
//
fn fitness(&self) -> Self::Fitness;
fn mate(&self, other: &Self) -> Self;
fn mutate(&mut self);
} }
pub trait Select<T>: Send pub trait Phenotype {
where //
T: Individual,
{
fn select(&self, population: Vec<Wrapper<T>>) -> Vec<Wrapper<T>>;
} }
*/
pub struct MaximizeSelector { pub mod crossover {
count: usize, use std::cmp;
}
impl MaximizeSelector { use rand::distributions::Uniform;
pub fn new(count: usize) -> Self { use rand::Rng;
MaximizeSelector { count }
pub fn single_point<T, R: Rng>(rng: &mut R, left: &[T], right: &[T]) -> (Vec<T>, Vec<T>)
where
T: Clone,
{
let min_len = cmp::min(left.len(), right.len());
let cross_point = rng.sample(Uniform::from(1..min_len));
let first = left[..cross_point]
.iter()
.chain(right[cross_point..].iter())
.cloned()
.collect::<Vec<_>>();
let second = right[..cross_point]
.iter()
.chain(left[cross_point..].iter())
.cloned()
.collect::<Vec<_>>();
(first, second)
} }
} }
impl<T> Select<T> for MaximizeSelector #[cfg(test)]
where mod tests {
T: Individual, use std::cmp::Ordering;
{ use std::f64;
fn select(&self, mut population: Vec<Wrapper<T>>) -> Vec<Wrapper<T>> {
population.sort_unstable_by(|a, b| b.fitness.cmp(&a.fitness));
population.into_iter().take(self.count).collect() use super::*;
}
}
pub struct MinimizeSelector { // use approx::assert_relative_eq;
count: usize, use rand::distributions::{Standard, Uniform};
} use rand::{thread_rng, Rng};
impl MinimizeSelector { #[test]
pub fn new(count: usize) -> Self { fn single_objective_sphere() {
MinimizeSelector { count } let mut rng = thread_rng();
}
}
impl<T> Select<T> for MinimizeSelector fn evaluate(x: &[f64]) -> f64 {
where x.iter().map(|x| x.powi(2)).sum()
T: Individual, }
{
fn select(&self, mut population: Vec<Wrapper<T>>) -> Vec<Wrapper<T>> {
population.sort_unstable_by(|a, b| a.fitness.cmp(&b.fitness));
population.into_iter().take(self.count).collect() fn generate<R: Rng>(rng: &mut R) -> Vec<f64> {
} let between = Uniform::new_inclusive(-10.0, 10.0);
}
#[derive(Clone)] (0..10).map(|_| rng.sample(between)).collect::<Vec<_>>()
pub struct Wrapper<T> }
where
T: Individual,
{
pub individual: T,
pub fitness: Option<T::Fitness>,
}
pub struct Simulation<T> fn mutate<R: Rng>(rng: &mut R, individual: &mut Vec<f64>) {
where let idx = rng.gen_range(0, individual.len());
T: Individual,
{
pub population: Vec<Wrapper<T>>,
}
impl<T> Simulation<T> individual[idx] += rng.sample(Uniform::new_inclusive(-1.0, 1.0));
where }
T: Individual,
{
pub fn with_population(initial: Vec<T>) -> Self {
let mut population = Vec::with_capacity(initial.len() * 2);
initial // step 1 (generate)
.into_iter() let mut population = (0..100)
.map(|individual| Wrapper { .map(|_| (generate(&mut rng), f64::MAX))
individual, .collect::<Vec<_>>();
fitness: None,
}) for _ in 0..1000 {
.for_each(|wrapper| { // step 2 (evaluate)
population.push(wrapper); population
.iter_mut()
.for_each(|(individual, fitness)| *fitness = evaluate(&individual));
population
.sort_unstable_by(|(_, a), (_, b)| a.partial_cmp(&b).unwrap_or(Ordering::Equal));
// step 3 (select)
let parents = population
.iter()
.step_by(2)
.zip(population.iter().skip(1).step_by(2))
.take(5)
.map(|((a, _), (b, _))| (a, b))
.collect::<Vec<_>>();
// step 4 (crossover)
let mut children = Vec::with_capacity(parents.len() * 2);
parents
.into_iter()
.map(|(left, right)| crossover::single_point(&mut rng, left, right))
.for_each(|(first, second)| {
children.push((first, f64::MAX));
children.push((second, f64::MAX));
});
// step 5 (survivial pressure)
population.truncate(population.len() - children.len());
// step 6 (mutate)
population.iter_mut().for_each(|(individual, fitness)| {
let random: f64 = rng.sample(Standard);
if random < 0.3 {
mutate(&mut rng, individual);
*fitness = f64::MAX;
}
}); });
Simulation { population } // step 7 (add children)
} population.extend_from_slice(&children);
}
#[inline] population
pub fn calculate(&mut self) {
self.population
.iter_mut() .iter_mut()
.filter(|wrapper| wrapper.fitness.is_none()) .for_each(|(individual, fitness)| *fitness = evaluate(&individual));
.for_each(|wrapper| wrapper.fitness = Some(wrapper.individual.fitness()));
self.population population.sort_unstable_by(|(_, a), (_, b)| a.partial_cmp(&b).unwrap_or(Ordering::Equal));
.sort_unstable_by(|a, b| b.fitness.cmp(&a.fitness));
}
pub fn evolve<F>(&mut self, func: F) println!("{:#?}", population);
where
F: Fn(&[Wrapper<T>], &mut Vec<Wrapper<T>>),
{
let mut population = Vec::with_capacity(self.population.len());
func(&self.population, &mut population); assert!(false);
self.population = population;
self.calculate();
}
}
pub struct ParSimulation<T>
where
T: Individual,
{
pub population: Vec<Wrapper<T>>,
}
impl<T> ParSimulation<T>
where
T: Individual,
{
pub fn with_population(initial: Vec<T>) -> Self {
let mut population = Vec::with_capacity(initial.len() * 2);
initial
.into_par_iter()
.map(|individual| Wrapper {
individual,
fitness: None,
})
.collect_into_vec(&mut population);
ParSimulation { 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<F>(&mut self, func: F)
where
F: Fn(&[Wrapper<T>], &mut Vec<Wrapper<T>>),
{
let mut population = Vec::with_capacity(self.population.len());
func(&self.population, &mut population);
self.population = population;
self.calculate();
} }
} }