use radiate_core::{
Chromosome, Objective, Population, Select, math::indexes, pareto, random_provider,
};
const NSGA2_SELECTOR_NAME: &str = "nsga2_selector";
#[derive(Debug, Clone)]
pub struct NSGA2Selector;
impl NSGA2Selector {
pub fn new() -> Self {
NSGA2Selector
}
}
impl<C: Chromosome + Clone> Select<C> for NSGA2Selector {
fn name(&self) -> &'static str {
NSGA2_SELECTOR_NAME
}
fn select(
&self,
population: &Population<C>,
objective: &Objective,
count: usize,
) -> Population<C> {
let scores = population.get_scores().collect::<Vec<_>>();
let ranks = pareto::rank(&scores, objective);
let distances = pareto::crowding_distance(&scores);
let mut indices = (0..population.len()).collect::<Vec<usize>>();
indices.sort_by(|&a, &b| {
let a_rank = ranks[a];
let b_rank = ranks[b];
let a_distance = distances[a];
let b_distance = distances[b];
if a_rank < b_rank || (a_rank == b_rank && a_distance > b_distance) {
std::cmp::Ordering::Less
} else if b_rank < a_rank || (b_rank == a_rank && b_distance > a_distance) {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
});
indices
.iter()
.take(count)
.map(|&i| population[i].clone())
.collect::<Population<C>>()
}
}
#[derive(Debug, Clone)]
pub struct TournamentNSGA2Selector;
impl TournamentNSGA2Selector {
pub fn new() -> Self {
TournamentNSGA2Selector
}
}
impl<C: Chromosome + Clone> Select<C> for TournamentNSGA2Selector {
fn select(
&self,
population: &Population<C>,
objective: &Objective,
count: usize,
) -> Population<C> {
let scores = population.get_scores().collect::<Vec<_>>();
let ranks = pareto::rank(&scores, objective);
let distances = pareto::crowding_distance(&scores);
let mut indices = (0..population.len()).collect::<Vec<usize>>();
indices.sort_by(|&a, &b| {
let a_rank = ranks[a];
let b_rank = ranks[b];
let a_distance = distances[a];
let b_distance = distances[b];
if a_rank < b_rank || (a_rank == b_rank && a_distance > b_distance) {
std::cmp::Ordering::Less
} else if b_rank < a_rank || (b_rank == a_rank && b_distance > a_distance) {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
});
let mut result = Vec::new();
while result.len() < count {
let k = std::cmp::min(2 * count - result.len(), population.len());
let mut g = vec![0; k];
indexes::subset(
population.len(),
k,
&mut g,
indexes::SubsetMode::StratifiedCorrect,
);
for i in 0..g.len() {
if result.len() >= count {
break;
}
let one = g[i];
let two = g[i + 1];
let desired_index = if indices[one] < indices[two] {
one
} else if indices[one] > indices[two] {
two
} else {
*random_provider::choose(&[one, two])
};
let pop_list = scores
.iter()
.enumerate()
.filter(|pair| pair.1 == &scores[desired_index])
.map(|pair| pair.0)
.collect::<Vec<_>>();
let random_phenotype = *random_provider::choose(&pop_list);
result.push(population[random_phenotype].clone());
}
}
result.into_iter().take(count).collect::<Population<C>>()
}
}