~ruther/ctu-fee-eoa

ref: 4ef3d988cfcefcd624d43dff088b456af928cefa ctu-fee-eoa/codes/eoa_lib/src/evolution.rs -rw-r--r-- 5.7 KiB
4ef3d988 — Rutherther tests: add simple test for evolution on one_max a month ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use std::error::Error;

use crate::{comparison::BetterThanOperator, crossover::Crossover, fitness::FitnessFunction, pairing::Pairing, perturbation::PerturbationOperator, replacement::{EvaluatedChromosome, EvaluatedPopulation, Population, Replacement}, selection::Selection};

#[derive(Clone, Debug)]
pub struct EvolutionCandidate<TInput, TResult> {
    pub evaluated_chromosome: EvaluatedChromosome<TInput, TResult>,
    pub iteration: usize
}

#[derive(Clone, Debug)]
pub struct EvolutionCandidatePopulation<TInput, TResult> {
    pub current_population: EvaluatedPopulation<TInput, TResult>,
    pub iteration: usize
}

#[derive(Clone, Debug)]
pub struct EvolutionStats<TInput, TResult> {
    pub best_candidates: Vec<EvolutionCandidate<TInput, TResult>>,
}

#[derive(Clone, Debug)]
pub struct EvolutionResult<TInput, TResult> {
    pub population: EvaluatedPopulation<TInput, TResult>,
    pub stats: EvolutionStats<TInput, TResult>,
    pub best_candidate: EvaluatedChromosome<TInput, TResult>,
    pub iterations: usize
}

pub fn evolution_algorithm<TChromosome: Clone, TResult: Clone>(
    initial_population: Population<TChromosome>,
    parents_count: usize,
    fitness: &impl FitnessFunction<In = TChromosome, Out = TResult>,
    selection: &mut impl Selection<TChromosome, TResult>,
    pairing: &mut impl Pairing<Chromosome = TChromosome, Out = TResult>,
    crossover: &mut impl Crossover<Chromosome = TChromosome, Out = TResult>,
    perturbation: &mut impl PerturbationOperator<Chromosome = TChromosome>,
    replacement: &mut impl Replacement<TChromosome, TResult>,
    better_than: &impl BetterThanOperator<TResult>,
    // TODO: termination condition
    iterations: usize
) -> Result<EvolutionResult<TChromosome, TResult>, Box<dyn Error>> {
    let mut current_population = initial_population.evaluate(fitness)?;

    let mut last_best_candidate = EvolutionCandidate {
        evaluated_chromosome: current_population.best_candidate(better_than).clone(),
        iteration: 0
    };
    let mut stats: EvolutionStats<TChromosome, TResult> = EvolutionStats {
        best_candidates: vec![]
    };

    for iteration in 0..iterations {
        // Figure out best candidate and save it if better than last time
        let best_candidate = current_population.best_candidate(better_than);

        if better_than.better_than(
            &best_candidate.evaluation,
            &last_best_candidate.evaluated_chromosome.evaluation
        ) {
            stats.best_candidates.push(last_best_candidate);

            last_best_candidate = EvolutionCandidate {
                evaluated_chromosome: best_candidate.clone(),
                iteration
            }
        }

        // Selection
        let parents = selection.select(parents_count, &current_population, better_than);
        let parent_pairings = pairing.pair(&current_population, parents);

        // Crossover
        let mut offsprings = crossover.crossover(&current_population, parent_pairings);

        // Mutation
        for offspring in offsprings.iter_mut() {
            perturbation.perturb(offspring);
        }

        let evaluated_offsprings = offsprings.evaluate(fitness)?;

        // Replace
        current_population = replacement.replace(current_population, evaluated_offsprings, better_than);
    }

    let best_candidate = last_best_candidate.evaluated_chromosome.clone();
    stats.best_candidates.push(last_best_candidate);

    Ok(EvolutionResult {
        population: current_population,
        best_candidate,
        stats,
        iterations
    })
}

#[cfg(test)]
pub mod tests {
    use nalgebra::Const;

    use crate::{binary_string::BinaryString, comparison::MinimizingOperator, crossover::BinaryOnePointCrossover, fitness::one_max::OneMax, initializer::{Initializer, RandomInitializer}, pairing::AdjacentPairing, perturbation::{BinaryStringBitPerturbation, MutationPerturbation}, replacement::{BestReplacement, Population, TournamentReplacement}, selection::{BestSelection, TournamentSelection}};

    use super::evolution_algorithm;

    #[test]
    pub fn test_one_max() {
        const D: usize = 512;
        let optimum = BinaryString::<Const<D>>::new(vec![0; D]);
        let one_max = OneMax::<Const<D>>::new();

        let mut initializer = RandomInitializer::<Const<D>, BinaryString::<Const<D>>>::new_binary();
        let population_size = 512;

        let population = Population::from_iterator(
            initializer.initialize(Const::<D>, population_size)
        );

        let result = evolution_algorithm(
            population,
            population_size / 4,
            &one_max,
            // TODO: tournament should somehow accept sorting?
            // TODO: deterministic and nondeterministic tournament ordering
            &mut TournamentSelection::new(3, 0.8),
            &mut AdjacentPairing::new(),
            &mut BinaryOnePointCrossover::new(),
            &mut MutationPerturbation::new(
                Box::new(BinaryStringBitPerturbation::new(0.05)),
                0.1),
            &mut BestReplacement::new(),
            &MinimizingOperator,
            1000
        ).unwrap();

        println!("{:?}", result.stats.best_candidates
                 .iter()
                 .map(|candidate| candidate.evaluated_chromosome.evaluation)
                 .collect::<Vec<_>>());
        println!("{:?}", result.stats.best_candidates
                 .iter()
                 .map(|candidate| candidate.iteration)
                 .collect::<Vec<_>>());
        println!("{:?}", result.population.best_candidate(&MinimizingOperator));
        println!("{:?}", result.best_candidate);
        // println!("{:?}", res);

        assert_eq!(
            result.best_candidate.evaluation,
            0
        );

        assert_eq!(
            result.best_candidate.chromosome,
            optimum
        );

    }
}