ge/
lib.rs

1//! Genetic algorithm tools
2
3extern crate rand;
4use rand::Rng;
5
6mod genome;
7pub use self::genome::Genome;
8
9/// Genetic algorithm
10///
11/// # Arguments
12/// * `pop_size`: number of individuals in the population
13/// * `n_iter`: number of iterations to run algorithm
14/// * `replace_rate`: rate at which individuals will be repleaced with results
15///     of crossover
16/// * `mut_rate`: rate of mutation in individuals
17/// * `rng`: Random number generator that will be used to get randomness
18///
19/// # Return
20/// The final generation of `n_iter` iterations, zipped with its fitness, sorted
21/// by fitness
22pub fn genetic_algorithm<G, R, O>(
23    pop_size:     usize,
24    n_iter:       usize,
25    replace_rate: f32,
26    mutate_rate:  f32,
27    mut rng:      R
28    ) -> Vec<(G, O)>
29where G: Genome + Clone,
30      R: Rng,
31      O: Ord,
32{
33    // initial population, zipped with fitness and sorted
34    let mut pop = init_generation(pop_size, &mut rng);
35
36    // run survival of the fittest simulation
37    for _ in 0..n_iter {
38        pop = next_generation(pop, replace_rate, mutate_rate, &mut rng);
39    }
40
41    pop
42}
43
44/// Create an initial generation of specified size
45///
46/// # Arguments
47/// * `size`: size of population
48/// * `rng`: Random number generator to pull randomness from
49///
50/// # Return
51/// An entirely random generation, sorted by fitness
52fn init_generation<G, R, O>(size: usize, rng: &mut R) -> Vec<(G, O)>
53where G: Genome,
54      R: Rng,
55      O: Ord
56{
57    let mut gen: Vec<(G, O)> = Vec::with_capacity(size);
58    for _ in 0..size {
59        let indv = G::genesis(rng);
60        let fitness = indv.fitness();
61        gen.push((indv, fitness));
62    }
63
64    gen.sort_by(|a, b| a.1.cmp(&b.1));
65    gen
66}
67
68/// Create the next generation
69///
70/// # Arguments
71/// * `gen`: base generation
72/// * `replace_rate`: fraction of population that will be replaced by the
73///     results of cross-over
74/// * `mutate_rate`: Rate of mutation in individuals
75/// * `rng`: Random number generator to pull randomness from
76///
77/// # Return
78/// Successor generation to `gen, zipped with, and sorted by fitness
79fn next_generation<G, R, O>(
80    gen:          Vec<(G, O)>,
81    replace_rate: f32,
82    mutate_rate:  f32,
83    rng:          &mut R
84    ) -> Vec<(G, O)>
85where G: Genome + Clone,
86      R: Rng,
87      O: Ord,
88{
89    let mut new_gen: Vec<G> = Vec::with_capacity(gen.len());
90
91    // Copy over the top performers
92    let highest_idx = ((1f32 - replace_rate) * new_gen.len() as f32) as usize;
93    for i in 0..highest_idx {
94        new_gen[i] = gen[i].0.clone();
95    }
96    
97    // create a mapping between candidates, this gaurentees no "self-love" will
98    // occur
99    let crosses = new_gen.len() - highest_idx;
100    let mut mapping: Vec<usize> = (0..crosses).collect();
101    rng.shuffle(&mut mapping);
102
103    // cross over the top candidates with eachother
104    for (i, j) in mapping.into_iter().enumerate() {
105        // TODO: I would like this to not clone the two operands
106        let (a, b) = (new_gen[i].clone(), new_gen[j].clone());
107        new_gen.push(a.cross(&b, rng));
108    }
109
110    // Mutate step
111    for e in new_gen.iter_mut() {
112        *e = e.mutate(mutate_rate, rng);
113    }
114
115    let mut zipped_with_fit: Vec<(G, O)> = new_gen.into_iter()
116        .map(|x| {
117            let fitness = x.fitness();
118            (x, fitness)
119        }).collect();
120
121    zipped_with_fit.sort_by(|a, b| a.1.cmp(&b.1));
122    zipped_with_fit
123}
124