Skip to content

Commit 241e5a2

Browse files
committed
Add Population unique_chromosome_indices and best_unique_chromosome_indices functions
1 parent 7b982c7 commit 241e5a2

File tree

2 files changed

+147
-1
lines changed

2 files changed

+147
-1
lines changed

src/population.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
//! The population is a container for [Chromosomes](Chromosome)
2-
use crate::chromosome::Chromosome;
2+
use crate::chromosome::{Chromosome, GenesHash};
33
use crate::fitness::{FitnessOrdering, FitnessValue};
44
use cardinality_estimator::CardinalityEstimator;
5+
use itertools::Itertools;
56
use rand::prelude::*;
67
use std::cmp::Reverse;
8+
use std::collections::HashMap;
79

810
#[derive(Clone, Debug)]
911
pub struct Population<C: Chromosome> {
@@ -65,6 +67,7 @@ impl<C: Chromosome> Population<C> {
6567

6668
// Returns one less than total size with known fitness due to implementation constraints
6769
// Doesn't matter the amount should be much less than the population size
70+
// Does not care about uniqueness of the genes_hash
6871
pub fn best_chromosome_indices(
6972
&self,
7073
amount: usize,
@@ -95,6 +98,57 @@ impl<C: Chromosome> Population<C> {
9598
}
9699
}
97100

101+
// Only works when genes_hash is stored on chromosome, as this is the uniqueness key.
102+
// Takes the first index occurence of a genes_hash. Returns indices in ascending order
103+
pub fn unique_chromosome_indices(&self) -> Vec<usize> {
104+
let mut data: HashMap<GenesHash, usize> = HashMap::new();
105+
self.chromosomes
106+
.iter()
107+
.enumerate()
108+
.for_each(|(index, chromosome)| {
109+
if let Some(genes_hash) = chromosome.genes_hash() {
110+
data.entry(genes_hash).or_insert_with(|| index);
111+
}
112+
});
113+
data.into_values().sorted().collect()
114+
}
115+
116+
// Only works when genes_hash is stored on chromosome, as this is the uniqueness key.
117+
// Assume chromosomes sorted by fitness, takes the first index occurence of a genes_hash
118+
// Returns indices in ascending order (irrespective of fitness)
119+
pub fn best_unique_chromosome_indices(
120+
&self,
121+
amount: usize,
122+
fitness_ordering: FitnessOrdering,
123+
) -> Vec<usize> {
124+
let mut data: HashMap<GenesHash, (usize, isize)> = HashMap::new();
125+
self.chromosomes
126+
.iter()
127+
.enumerate()
128+
.for_each(|(index, chromosome)| {
129+
if let Some(genes_hash) = chromosome.genes_hash() {
130+
if let Some(fitness_score) = chromosome.fitness_score() {
131+
data.entry(genes_hash)
132+
.or_insert_with(|| (index, fitness_score));
133+
}
134+
}
135+
});
136+
137+
if data.is_empty() {
138+
Vec::new()
139+
} else {
140+
let iterator = match fitness_ordering {
141+
FitnessOrdering::Maximize => data
142+
.into_values()
143+
.sorted_unstable_by_key(|(_, score)| Reverse(*score)),
144+
FitnessOrdering::Minimize => data
145+
.into_values()
146+
.sorted_unstable_by_key(|(_, score)| *score),
147+
};
148+
iterator.take(amount).map(|(idx, _)| idx).sorted().collect()
149+
}
150+
}
151+
98152
pub fn age_mean(&self) -> f32 {
99153
stats::mean(self.chromosomes.iter().map(|c| c.age())) as f32
100154
}

tests/population_test.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,98 @@ mod population_tests {
177177
);
178178
}
179179

180+
#[test]
181+
fn unique_chromosome_indices() {
182+
let genotype = BinaryGenotype::builder()
183+
.with_genes_size(3)
184+
.with_genes_hashing(true)
185+
.build()
186+
.unwrap();
187+
188+
let mut population: Population<BinaryChromosome> =
189+
build::population_with_fitness_scores(vec![
190+
(vec![false, true, true], Some(2)),
191+
(vec![false, true, true], Some(2)),
192+
(vec![false, false, false], Some(0)),
193+
(vec![true, true, true], Some(3)),
194+
(vec![false, false, false], Some(0)),
195+
(vec![true, true, true], Some(3)),
196+
(vec![false, false, true], Some(1)),
197+
(vec![false, false, true], Some(1)),
198+
(vec![true, true, false], None),
199+
(vec![true, true, false], None),
200+
]);
201+
202+
population.chromosomes.iter_mut().for_each(|chromosome| {
203+
let genes_hash = genotype.calculate_genes_hash(chromosome);
204+
chromosome.set_genes_hash(genes_hash);
205+
});
206+
207+
assert_eq!(population.unique_chromosome_indices(), vec![0, 2, 3, 6, 8]);
208+
}
209+
210+
#[test]
211+
fn best_unique_chromosome_indices() {
212+
let genotype = BinaryGenotype::builder()
213+
.with_genes_size(3)
214+
.with_genes_hashing(true)
215+
.build()
216+
.unwrap();
217+
218+
let mut population: Population<BinaryChromosome> =
219+
build::population_with_fitness_scores(vec![
220+
(vec![false, true, true], Some(2)),
221+
(vec![false, true, true], Some(2)),
222+
(vec![false, false, false], Some(0)),
223+
(vec![true, true, true], Some(3)),
224+
(vec![false, false, false], Some(0)),
225+
(vec![true, true, true], Some(3)),
226+
(vec![false, false, true], Some(1)),
227+
(vec![false, false, true], Some(1)),
228+
(vec![true, true, false], None),
229+
(vec![true, true, false], None),
230+
]);
231+
232+
population.chromosomes.iter_mut().for_each(|chromosome| {
233+
let genes_hash = genotype.calculate_genes_hash(chromosome);
234+
chromosome.set_genes_hash(genes_hash);
235+
});
236+
237+
assert_eq!(
238+
population.best_unique_chromosome_indices(2, FitnessOrdering::Maximize),
239+
vec![0, 3]
240+
);
241+
assert_eq!(
242+
population.best_unique_chromosome_indices(1, FitnessOrdering::Maximize),
243+
vec![3]
244+
);
245+
assert_eq!(
246+
population.best_unique_chromosome_indices(0, FitnessOrdering::Maximize),
247+
vec![]
248+
);
249+
assert_eq!(
250+
population.best_unique_chromosome_indices(10, FitnessOrdering::Maximize),
251+
vec![0, 2, 3, 6]
252+
);
253+
254+
assert_eq!(
255+
population.best_unique_chromosome_indices(2, FitnessOrdering::Minimize),
256+
vec![2, 6]
257+
);
258+
assert_eq!(
259+
population.best_unique_chromosome_indices(1, FitnessOrdering::Minimize),
260+
vec![2]
261+
);
262+
assert_eq!(
263+
population.best_unique_chromosome_indices(0, FitnessOrdering::Minimize),
264+
vec![]
265+
);
266+
assert_eq!(
267+
population.best_unique_chromosome_indices(10, FitnessOrdering::Minimize),
268+
vec![0, 2, 3, 6]
269+
);
270+
}
271+
180272
#[test]
181273
fn fitness_score_cardinality() {
182274
let population: Population<BinaryChromosome> = build::population(vec![

0 commit comments

Comments
 (0)