#![feature(num_bits_bytes)]
extern crate bit_vec;
extern crate rand;
extern crate simple_parallel;
use rand::{Rand, Rng};
use std::cmp::PartialOrd;
use simple_parallel::Pool;
pub mod bit_string;
#[derive(Copy, Clone, Debug)]
pub struct Probability(f32);
impl Probability {
#[inline]
pub fn new(pb: f32) -> Probability {
assert!(pb >= 0.0 && pb <= 1.0);
Probability(pb)
}
}
impl std::ops::Add<Probability> for Probability {
type Output = Probability;
#[inline]
fn add(self, rhs: Probability) -> Probability {
Probability::new(self.0 + rhs.0)
}
}
#[derive(Copy, Clone, Debug)]
pub struct ProbabilityValue(f32);
impl Rand for ProbabilityValue {
#[inline]
fn rand<R: Rng>(rng: &mut R) -> ProbabilityValue {
let val = rng.gen::<f32>();
assert!(val >= 0.0 && val < 1.0);
ProbabilityValue(val)
}
}
impl ProbabilityValue {
#[inline]
pub fn is_probable_with(self, pb: Probability) -> bool {
self.0 < pb.0
}
}
pub trait Fitness: Clone+Send {
fn fitter_than(&self, other: &Self) -> bool;
}
#[derive(Copy, Clone, Debug)]
pub struct MaxFitness<T: PartialOrd + Clone + Send>(pub T);
impl<T:PartialOrd+Clone+Send> Fitness for MaxFitness<T> {
#[inline(always)]
fn fitter_than(&self, other: &MaxFitness<T>) -> bool {
self.0 > other.0
}
}
#[derive(Copy, Clone, Debug)]
pub struct MinFitness<T: PartialOrd + Clone + Send>(pub T);
impl<T:PartialOrd+Clone+Send> Fitness for MinFitness<T> {
#[inline(always)]
fn fitter_than(&self, other: &MinFitness<T>) -> bool {
self.0 < other.0
}
}
pub trait Individual: Clone+Send {
}
pub trait Evaluator<I:Individual, F:Fitness>: Sync {
fn fitness(&self, individual: &I) -> F;
}
#[derive(Clone, Debug)]
pub struct EvaluatedIndividual<I: Individual, F: Fitness> {
individual: I,
fitness: Option<F>,
}
impl<I:Individual, F:Fitness> EvaluatedIndividual<I,F> {
pub fn new(ind: I) -> EvaluatedIndividual<I, F> {
EvaluatedIndividual {
individual: ind,
fitness: None,
}
}
}
#[derive(Clone, Debug)]
pub struct Population<I: Individual, F: Fitness> {
population: Vec<EvaluatedIndividual<I, F>>,
}
impl<I:Individual, F:Fitness> Population<I,F>
{
pub fn new() -> Population<I, F> {
Population { population: Vec::new() }
}
pub fn with_capacity(capa: usize) -> Population<I, F> {
Population { population: Vec::with_capacity(capa) }
}
#[inline(always)]
pub fn len(&self) -> usize {
self.population.len()
}
pub fn fittest(&self) -> Option<(usize, F)> {
let mut fittest: Option<(usize, F)> = None;
for (i, ref ind) in self.population.iter().enumerate() {
if let Some(ref f) = ind.fitness {
let mut is_better = true;
if let Some((_, ref best_f)) = fittest {
if !f.fitter_than(best_f) {
is_better = false;
}
}
if is_better {
fittest = Some((i, f.clone()));
}
}
}
return fittest;
}
#[inline]
pub fn get_individual(&self, idx: usize) -> &I {
&self.population[idx].individual
}
#[inline]
pub fn get_fitness(&self, idx: usize) -> F {
(&self.population[idx]).fitness.clone().unwrap()
}
pub fn add_individual(&mut self, ind: I) {
self.population.push(EvaluatedIndividual::new(ind));
}
pub fn add_individual_with_fitness(&mut self, ind: I, fitness: F) {
self.population.push(EvaluatedIndividual{individual: ind, fitness: Some(fitness)});
}
pub fn evaluate<E>(&mut self, evaluator: &E) -> usize
where E: Evaluator<I, F>
{
let mut nevals = 0;
for i in self.population.iter_mut() {
if i.fitness.is_some() {
continue;
}
i.fitness = Some(evaluator.fitness(&i.individual));
nevals += 1;
}
return nevals;
}
pub fn evaluate_in_parallel<E>(&mut self, evaluator: &E, pool: &mut Pool, chunk_size: usize) -> usize
where E: Evaluator<I, F>
{
let mut nevals = 0;
for i in self.population.iter() {
if i.fitness.is_none() {
nevals += 1;
}
}
pool.for_(self.population.chunks_mut(chunk_size), |chunk| {
for ind in chunk.iter_mut() {
if ind.fitness.is_some() { continue; }
ind.fitness = Some(evaluator.fitness(&ind.individual));
}
});
return nevals;
}
fn extend_with(&mut self, p: Population<I, F>) {
self.population.extend(p.population);
}
}
#[inline]
pub fn tournament_selection<R: Rng, F, E>(rng: &mut R,
fitness: E,
n: usize,
k: usize)
-> Option<(usize, F)>
where F: Fitness,
E: Fn(usize) -> F
{
assert!(n >= k);
let mut best: Option<(usize, F)> = None;
for _ in 0..k {
let i = rng.gen_range(0, n);
let fitness = fitness(i);
let better = match best {
Some((_, ref current_best_fitness)) => {
fitness.fitter_than(current_best_fitness)
}
None => {
true
}
};
if better {
best = Some((i, fitness));
}
}
return best;
}
pub enum VariationMethod {
Crossover,
Mutation,
Reproduction,
}
pub trait OpCrossover<I: Individual> {
fn crossover(&mut self, parent1: &I, parent2: &I) -> (I, I);
}
pub trait OpMutate<I: Individual> {
fn mutate(&mut self, ind: &I) -> I;
}
pub trait OpVariation {
fn variation(&mut self) -> VariationMethod;
}
pub trait OpSelectRandomIndividual<I: Individual, F: Fitness> {
fn select_random_individual<'a>(&mut self,
population: &'a Population<I, F>)
-> usize; }
pub trait OpSelect<I: Individual, F: Fitness> {
fn select(&mut self, population: &Population<I, F>, mu: usize) -> Population<I, F>;
}
pub fn variation_or<I, F, T>(toolbox: &mut T,
population: &Population<I, F>,
lambda: usize)
-> (Population<I,F>, Population<I,F>)
where I: Individual,
F: Fitness,
T: OpCrossover<I> + OpMutate<I> + OpVariation + OpSelectRandomIndividual<I, F>
{
let mut unrated_offspring = Population::with_capacity(lambda);
let mut rated_offspring = Population::new();
for _ in 0..lambda {
let method = toolbox.variation();
match method {
VariationMethod::Crossover => {
let parent1_idx = toolbox.select_random_individual(population);
let parent2_idx = toolbox.select_random_individual(population);
let (child1, _child2) = toolbox.crossover(population.get_individual(parent1_idx), population.get_individual(parent2_idx));
unrated_offspring.add_individual(child1);
}
VariationMethod::Mutation => {
let ind_idx = toolbox.select_random_individual(population);
let child = toolbox.mutate(population.get_individual(ind_idx));
unrated_offspring.add_individual(child);
}
VariationMethod::Reproduction => {
let ind_idx = toolbox.select_random_individual(population);
rated_offspring.add_individual_with_fitness(population.get_individual(ind_idx).clone(), population.get_fitness(ind_idx));
}
}
}
return (unrated_offspring, rated_offspring);
}
#[inline]
pub fn ea_mu_plus_lambda<I,F,T,E,S>(toolbox: &mut T, evaluator: &E, mut population: Population<I,F>, mu: usize, lambda: usize, num_generations: usize, stat: S, numthreads: usize, chunksize: usize)
-> Population<I,F>
where I: Individual,
F: Fitness,
T: OpCrossover<I> + OpMutate<I> + OpVariation + OpSelectRandomIndividual<I,F> + OpSelect<I,F>,
E: Evaluator<I,F>+Sync,
S: Fn(usize, usize, &Population<I,F>)
{
let mut pool = simple_parallel::Pool::new(numthreads);
let nevals = population.evaluate_in_parallel(evaluator, &mut pool, chunksize);
stat(0, nevals, &population);
for gen in 0..num_generations {
let (mut unrated_offspring, rated_offspring) = variation_or(toolbox, &population, lambda);
let nevals = unrated_offspring.evaluate_in_parallel(evaluator, &mut pool, chunksize);
population.extend_with(unrated_offspring); population.extend_with(rated_offspring);
let p = toolbox.select(&population, mu);
stat(gen + 1, nevals, &p);
population = p;
}
return population;
}