spdkit/
population.rs

1// [[file:../spdkit.note::*imports][imports:1]]
2use std::marker::PhantomData;
3
4use crate::common::*;
5use crate::fitness::*;
6use crate::individual::*;
7// imports:1 ends here
8
9// [[file:../spdkit.note::*base][base:1]]
10/// A population is a collection of evaluated individuals (fitness).
11#[derive(Clone, Debug)]
12pub struct Population<G>
13where
14    G: Genome,
15{
16    /// The individuals in this population.
17    individuals: Vec<Individual<G>>,
18
19    /// The max number of individuals allowed to be survived in this population.
20    size_limit: usize,
21
22    /// The fitness values of all individuals.
23    fitness_values: Vec<f64>,
24}
25
26impl<G> Population<G>
27where
28    G: Genome,
29{
30    /// Return a list of individuals in this population.
31    pub fn individuals(&self) -> &[Individual<G>] {
32        &self.individuals
33    }
34
35    /// Return population size.
36    pub fn size(&self) -> usize {
37        self.individuals.len()
38    }
39
40    /// Return true of there are too many individuals in this population.
41    pub fn is_oversized(&self) -> bool {
42        self.size() > self.size_limit
43    }
44
45    /// Return population size limit.
46    pub fn size_limit(&self) -> usize {
47        self.size_limit
48    }
49
50    /// Re-evaluate individuals in population with `fitness` function.
51    pub fn evaluate_with<F: EvaluateFitness<G>>(&mut self, fitness: &mut F) {
52        self.fitness_values = evaluate_individuals(&self.individuals, fitness);
53    }
54
55    /// Re-evaluate individual fitness with individual weight.
56    pub fn weight_with(&mut self, weight: f64) {
57        for fitness in self.fitness_values.iter_mut() {
58            *fitness *= weight;
59        }
60    }
61
62    /// Build a population from individuals `indvs` and fitness function.
63    /// The population size limit is set as the size of `indvs`.
64    pub fn build<F>(indvs: Vec<Individual<G>>, fitness: &mut F) -> Self
65    where
66        F: EvaluateFitness<G>,
67    {
68        let nlimit = indvs.len();
69        let fitness_values = evaluate_individuals(&indvs, fitness);
70
71        Self {
72            individuals: indvs,
73            fitness_values,
74            size_limit: nlimit,
75        }
76    }
77
78    /// Construct with population size limit.
79    pub fn with_size_limit(mut self, limit: usize) -> Self {
80        self.size_limit = limit;
81        self
82    }
83}
84
85/// Evaluate individuals with a fitness function.
86fn evaluate_individuals<G, F>(indvs: &[Individual<G>], fitness: &mut F) -> Vec<f64>
87where
88    G: Genome,
89    F: EvaluateFitness<G>,
90{
91    let fitness_values = fitness.evaluate(indvs);
92    assert_eq!(
93        fitness_values.len(),
94        indvs.len(),
95        "fitness values is not equal to the number of individuals!"
96    );
97    fitness_values
98}
99// base:1 ends here
100
101// [[file:../spdkit.note::*members][members:1]]
102/// A member is a view of individual with its evaluated fitness value in parent
103/// Population.
104#[derive(Debug, Clone)]
105pub struct Member<'a, G>
106where
107    G: Genome,
108{
109    pub individual: &'a Individual<G>,
110    fitness: f64,
111}
112
113pub trait SortMember {
114    fn sort_by_fitness(&mut self);
115}
116
117impl<'a, G> Member<'a, G>
118where
119    G: Genome,
120{
121    /// Return individual objective value.
122    pub fn objective_value(&self) -> f64 {
123        self.individual.objective_value()
124    }
125
126    /// Return individual fitness value.
127    pub fn fitness_value(&self) -> f64 {
128        self.fitness
129    }
130
131    /// Return a reference to individual genome.
132    pub fn genome(&self) -> &G {
133        self.individual.genome()
134    }
135}
136
137impl<'a, G> SortMember for [Member<'a, G>]
138where
139    G: Genome,
140{
141    fn sort_by_fitness(&mut self) {
142        self.sort_by(|mi, mj| {
143            let (fi, fj) = (mi.fitness, mj.fitness);
144            // puts NANs at the end
145            float_ordering_maximize(&fi, &fj)
146        });
147    }
148}
149
150/// Nice output for the Genome implemeting Display trait.
151impl<'a, G> std::fmt::Display for Member<'a, G>
152where
153    G: Genome + std::fmt::Display,
154{
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        write!(
157            f,
158            "indv {}: fitness = {:5.2} objective value = {}",
159            self.individual.genome(),
160            self.fitness,
161            self.individual.objective_value(),
162        )
163    }
164}
165
166impl<G> Population<G>
167where
168    G: Genome,
169{
170    /// Return a member view of individuals with associated fitness values.
171    pub fn members(&self) -> impl Iterator<Item = Member<G>> {
172        self.individuals
173            .iter()
174            .zip(self.fitness_values.iter())
175            .map(|(individual, &fitness)| Member { individual, fitness })
176    }
177
178    /// Return a member view of the best individual in this population.
179    pub fn best_member(&self) -> Option<Member<G>> {
180        self.fitness_values.iter().imax().and_then(|(i, fitness)| {
181            let best = Member {
182                individual: &self.individuals[i],
183                fitness,
184            };
185            Some(best)
186        })
187    }
188}
189// members:1 ends here
190
191// [[file:../spdkit.note::*survive][survive:1]]
192impl<G> Population<G>
193where
194    G: Genome,
195{
196    /// Remove some bad performing individuals to fit the population size limit
197    /// constrain.
198    ///
199    /// # Returns
200    ///
201    /// * return the number of individuals to be removed.
202    ///
203    pub fn survive(&mut self) -> usize {
204        if self.is_oversized() {
205            let n_old = self.size();
206            let mut members: Vec<_> = self.members().collect();
207            members.sort_by_fitness();
208            let to_keep: Vec<_> = members.into_iter().take(self.size_limit).collect();
209
210            let mut indvs = vec![];
211            let mut values = vec![];
212            for m in to_keep {
213                indvs.push(m.individual.to_owned());
214                values.push(m.fitness_value());
215            }
216
217            self.individuals = indvs;
218            self.fitness_values = values;
219            n_old - self.size()
220        } else {
221            0
222        }
223    }
224}
225// survive:1 ends here
226
227// [[file:../spdkit.note::*test][test:1]]
228#[cfg(test)]
229mod test {
230    use super::*;
231    use crate::encoding::Binary;
232
233    #[test]
234    fn test() {
235        let keys: Vec<_> = vec!["10110", "01010", "11011"].iter().map(|x| Binary::from_str(x)).collect();
236
237        let mut fitness = crate::fitness::Maximize;
238        let indvs = crate::individual::OneMax.create(keys);
239        let pop = Population::build(indvs, &mut fitness).with_size_limit(10);
240
241        assert!(!pop.is_oversized());
242
243        let mut members: Vec<_> = pop.members().collect();
244        members.sort_by_fitness();
245
246        for m in members.iter() {
247            // dbg!(m.individual, m.fitness_value());
248        }
249
250        let m = pop.best_member().unwrap();
251        assert_eq!(m.genome().to_string(), "11011");
252    }
253}
254// test:1 ends here