Did things
This commit is contained in:
13
Cargo.toml
13
Cargo.toml
@@ -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
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
@@ -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!();
|
|
||||||
}
|
|
||||||
239
src/lib.rs
239
src/lib.rs
@@ -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 {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub mod crossover {
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use rand::distributions::Uniform;
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
pub fn single_point<T, R: Rng>(rng: &mut R, left: &[T], right: &[T]) -> (Vec<T>, Vec<T>)
|
||||||
where
|
where
|
||||||
T: Individual,
|
T: Clone,
|
||||||
{
|
{
|
||||||
fn select(&self, population: Vec<Wrapper<T>>) -> Vec<Wrapper<T>>;
|
let min_len = cmp::min(left.len(), right.len());
|
||||||
}
|
let cross_point = rng.sample(Uniform::from(1..min_len));
|
||||||
|
|
||||||
pub struct MaximizeSelector {
|
let first = left[..cross_point]
|
||||||
count: usize,
|
.iter()
|
||||||
}
|
.chain(right[cross_point..].iter())
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
impl MaximizeSelector {
|
let second = right[..cross_point]
|
||||||
pub fn new(count: usize) -> Self {
|
.iter()
|
||||||
MaximizeSelector { count }
|
.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::*;
|
||||||
}
|
|
||||||
|
// use approx::assert_relative_eq;
|
||||||
|
use rand::distributions::{Standard, Uniform};
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_objective_sphere() {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
fn evaluate(x: &[f64]) -> f64 {
|
||||||
|
x.iter().map(|x| x.powi(2)).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MinimizeSelector {
|
fn generate<R: Rng>(rng: &mut R) -> Vec<f64> {
|
||||||
count: usize,
|
let between = Uniform::new_inclusive(-10.0, 10.0);
|
||||||
|
|
||||||
|
(0..10).map(|_| rng.sample(between)).collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MinimizeSelector {
|
fn mutate<R: Rng>(rng: &mut R, individual: &mut Vec<f64>) {
|
||||||
pub fn new(count: usize) -> Self {
|
let idx = rng.gen_range(0, individual.len());
|
||||||
MinimizeSelector { count }
|
|
||||||
}
|
individual[idx] += rng.sample(Uniform::new_inclusive(-1.0, 1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Select<T> for MinimizeSelector
|
// step 1 (generate)
|
||||||
where
|
let mut population = (0..100)
|
||||||
T: Individual,
|
.map(|_| (generate(&mut rng), f64::MAX))
|
||||||
{
|
.collect::<Vec<_>>();
|
||||||
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()
|
for _ in 0..1000 {
|
||||||
}
|
// step 2 (evaluate)
|
||||||
}
|
population
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|(individual, fitness)| *fitness = evaluate(&individual));
|
||||||
|
|
||||||
#[derive(Clone)]
|
population
|
||||||
pub struct Wrapper<T>
|
.sort_unstable_by(|(_, a), (_, b)| a.partial_cmp(&b).unwrap_or(Ordering::Equal));
|
||||||
where
|
|
||||||
T: Individual,
|
|
||||||
{
|
|
||||||
pub individual: T,
|
|
||||||
pub fitness: Option<T::Fitness>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Simulation<T>
|
// step 3 (select)
|
||||||
where
|
let parents = population
|
||||||
T: Individual,
|
.iter()
|
||||||
{
|
.step_by(2)
|
||||||
pub population: Vec<Wrapper<T>>,
|
.zip(population.iter().skip(1).step_by(2))
|
||||||
}
|
.take(5)
|
||||||
|
.map(|((a, _), (b, _))| (a, b))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
impl<T> Simulation<T>
|
// step 4 (crossover)
|
||||||
where
|
let mut children = Vec::with_capacity(parents.len() * 2);
|
||||||
T: Individual,
|
|
||||||
{
|
|
||||||
pub fn with_population(initial: Vec<T>) -> Self {
|
|
||||||
let mut population = Vec::with_capacity(initial.len() * 2);
|
|
||||||
|
|
||||||
initial
|
parents
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|individual| Wrapper {
|
.map(|(left, right)| crossover::single_point(&mut rng, left, right))
|
||||||
individual,
|
.for_each(|(first, second)| {
|
||||||
fitness: None,
|
children.push((first, f64::MAX));
|
||||||
})
|
children.push((second, f64::MAX));
|
||||||
.for_each(|wrapper| {
|
|
||||||
population.push(wrapper);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Simulation { population }
|
// 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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user