#[cfg(test)]
use crate::structures::{Chromosome, Gene};
use genetic_algorithms::{
fitness::FitnessFnWrapper, operations::selection::clearing::clearing_selection,
};
fn make_chromosome(fitness: f64, dna: Vec<Gene>) -> Chromosome {
Chromosome {
dna,
fitness,
age: 0,
fitness_fn: FitnessFnWrapper::default(),
fitness_values: vec![],
}
}
fn gene(id: i32) -> Gene {
Gene { id }
}
fn pop_distinct(fitnesses: &[f64]) -> Vec<Chromosome> {
fitnesses
.iter()
.map(|&f| make_chromosome(f, vec![gene(0)]))
.collect()
}
#[test]
fn test_clearing_returns_pairs_of_valid_indices() {
let pop = pop_distinct(&[10.0, 9.0, 5.0, 4.5, 0.0, 0.3]);
let pairs = clearing_selection(&pop, 0.5, 3, 2);
for group in &pairs {
assert!(group[0] < pop.len(), "Index {} out of bounds", group[0]);
assert!(group[1] < pop.len(), "Index {} out of bounds", group[1]);
assert_ne!(group[0], group[1], "Self-pairing not allowed");
}
}
#[test]
fn test_clearing_returns_no_pairs_for_single_chromosome() {
let pop = vec![make_chromosome(1.0, vec![gene(0)])];
let pairs = clearing_selection(&pop, 0.1, 1, 2);
assert!(
pairs.is_empty(),
"Cannot form pairs from a single individual"
);
}
#[test]
fn test_clearing_returns_no_pairs_for_empty_population() {
let pop: Vec<Chromosome> = Vec::new();
let pairs = clearing_selection(&pop, 0.1, 1, 2);
assert!(pairs.is_empty());
}
#[test]
fn test_clearing_clears_dominated_individuals_within_radius() {
let pop = vec![
make_chromosome(10.0, vec![gene(0)]),
make_chromosome(10.05, vec![gene(1)]),
make_chromosome(5.0, vec![gene(2)]),
];
let pairs = clearing_selection(&pop, 0.1, 1, 2);
assert_eq!(pairs.len(), 1);
for group in &pairs {
assert_ne!(
group[0], 0,
"Cleared individual (index 0) should not appear in pairs"
);
assert_ne!(
group[1], 0,
"Cleared individual (index 0) should not appear in pairs"
);
}
}
#[test]
fn test_clearing_preserves_one_winner_per_niche() {
let pop = vec![
make_chromosome(10.0, vec![gene(0)]),
make_chromosome(10.05, vec![gene(1)]),
make_chromosome(5.0, vec![gene(2)]),
make_chromosome(4.97, vec![gene(3)]),
];
let pairs = clearing_selection(&pop, 0.1, 1, 2);
assert_eq!(pairs.len(), 1);
}
#[test]
fn test_clearing_with_zero_radius_keeps_all_eligible() {
let pop = pop_distinct(&[4.0, 3.0, 2.0, 1.0]);
let pairs = clearing_selection(&pop, 0.0, 2, 2);
assert_eq!(pairs.len(), 2);
}
#[test]
fn test_clearing_with_large_radius_keeps_only_one_winner() {
let pop = pop_distinct(&[10.0, 5.0, 1.0]);
let pairs = clearing_selection(&pop, 100.0, 1, 2);
assert!(pairs.is_empty());
}
#[test]
fn test_clearing_via_factory_respects_niche_radius() {
use genetic_algorithms::{
configuration::SelectionConfiguration, operations::selection, operations::Selection,
};
let pop = vec![
make_chromosome(10.0, vec![gene(0)]),
make_chromosome(10.05, vec![gene(1)]),
make_chromosome(5.0, vec![gene(2)]),
];
let config = SelectionConfiguration {
method: Selection::Clearing,
number_of_couples: 3,
niche_radius: 0.1,
..Default::default()
};
let result = selection::factory(&pop, config, 1, 2);
assert!(result.is_ok());
let pairs = result.unwrap();
assert_eq!(pairs.len(), 3);
for group in &pairs {
assert_ne!(
group[0], 0,
"Cleared individual (index 0) should not appear"
);
assert_ne!(
group[1], 0,
"Cleared individual (index 0) should not appear"
);
assert_ne!(group[0], group[1], "Self-pairing not allowed");
}
}
#[test]
fn test_clearing_via_selection_enum() {
use genetic_algorithms::{operations::Selection, traits::SelectionOperator};
let pop = pop_distinct(&[10.0, 9.0, 5.0, 4.0]);
let pairs = Selection::Clearing
.select(&pop, 4, 1, 2)
.expect("clearing selection should succeed");
assert_eq!(pairs.len(), 4);
}