~ruther/ctu-fee-eoa

ref: c67cbe05b263f3d2f59d9217d0e112ff23b43d74 ctu-fee-eoa/codes/constr_hw02/src/feasible_crossover_wrapper.rs -rw-r--r-- 5.0 KiB
c67cbe05 — Rutherther Finish hw02 5 days 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
use eoa_lib::{
    crossover::Crossover,
    multi_objective_evolution::NSGAEvaluation,
    pairing,
    population::{EvaluatedChromosome, EvaluatedPopulation, Population},
};
use rand::{Rng, RngCore};

pub struct FeasibleCrossoverWrapper<
    const OBJECTIVES: usize,
    TChromosome,
    TCrossover: Crossover<2, Chromosome = TChromosome, Out = NSGAEvaluation<f64, OBJECTIVES>>,
> {
    // If there is just one infesiable, replace it
    // with this probability.
    pub p_single_replaced: f64,
    // If there are two infesiable, replace
    // first one with this probability
    pub p_double_first_replaced: f64,
    // If there are two infesiable, also
    // replace the second with this probability.
    // This is tried only if the first one is
    // replaced.
    pub p_double_second_replaced: f64,

    pub archived_count: usize,

    pub archived_population: Vec<EvaluatedChromosome<TChromosome, NSGAEvaluation<f64, OBJECTIVES>>>,
    pub crossover: TCrossover,
}

impl<
        const OBJECTIVES: usize,
        TChromosome: Clone,
        TCrossover: Crossover<2, Chromosome = TChromosome, Out = NSGAEvaluation<f64, OBJECTIVES>>,
    > FeasibleCrossoverWrapper<OBJECTIVES, TChromosome, TCrossover>
{
    pub fn update_archive(
        &mut self,
        population: &EvaluatedPopulation<TChromosome, NSGAEvaluation<f64, OBJECTIVES>>,
    ) {
        // Find all feasible solutions in population. Those are candidates
        // to replace in archive.

        // Lotta allocations...
        let mut feasible_individuals = population
            .population
            .iter()
            .filter(|individual| {
                individual
                    .evaluation
                    .evaluations
                    .iter()
                    .skip(1)
                    .all(|&constr| constr <= 0.0)
            })
            .cloned()
            .collect::<Vec<_>>();

        // TODO: this could definitely be allocated in a smarter way for better effectivity
        self.archived_population.append(&mut feasible_individuals);

        self.archived_population.sort_unstable_by(|a, b| {
            a.evaluation.evaluations[0].total_cmp(&b.evaluation.evaluations[0])
        });

        self.archived_population.truncate(self.archived_count);
    }
}

impl<
        const OBJECTIVES: usize,
        TChromosome: Clone,
        TCrossover: Crossover<2, Chromosome = TChromosome, Out = NSGAEvaluation<f64, OBJECTIVES>>,
    > Crossover<2> for FeasibleCrossoverWrapper<OBJECTIVES, TChromosome, TCrossover>
{
    type Chromosome = TChromosome;
    type Out = NSGAEvaluation<f64, OBJECTIVES>;

    fn crossover(
        &self,
        parents: &EvaluatedPopulation<Self::Chromosome, NSGAEvaluation<f64, OBJECTIVES>>,
        pairs: impl Iterator<Item = pairing::ParentPairing<2>>,
        rng: &mut dyn RngCore,
    ) -> Population<Self::Chromosome> {
        // Lotta allocations! :(

        let parents_count = parents.population.len();
        let mut joined_population = parents.clone();
        joined_population.join(EvaluatedPopulation::from_vec(
            self.archived_population.clone(),
        ));
        let full_population = joined_population.population.len();

        let mut new_pairs = pairs.collect::<Vec<_>>();

        for pair in new_pairs.iter_mut() {
            let a = &joined_population.population[pair[0]];
            let b = &joined_population.population[pair[1]];

            let a_feasible = a
                .evaluation
                .evaluations
                .iter()
                .skip(1)
                .all(|&constr| constr <= 0.0);
            let b_feasible = b
                .evaluation
                .evaluations
                .iter()
                .skip(1)
                .all(|&constr| constr <= 0.0);

            // Only proceed with replacement if we have archived feasible solutions
            if full_population > parents_count {
                match (a_feasible, b_feasible) {
                    (false, true) => {
                        if rng.random_bool(self.p_single_replaced) {
                            pair[0] = rng.random_range(parents_count..full_population);
                        }
                    }
                    (true, false) => {
                        if rng.random_bool(self.p_single_replaced) {
                            pair[1] = rng.random_range(parents_count..full_population);
                        }
                    }
                    (false, false) => {
                        if rng.random_bool(self.p_double_first_replaced) {
                            pair[0] = rng.random_range(parents_count..full_population);

                            if rng.random_bool(self.p_double_second_replaced) {
                                pair[1] = rng.random_range(parents_count..full_population);
                            }
                        }
                    }
                    (true, true) => {
                        // Do nothing.
                    }
                }
            }
        }

        self.crossover
            .crossover(&joined_population, new_pairs.into_iter(), rng)
    }
}