use std::marker::PhantomData;
use rand::prelude::IndexedRandom;
use rand::RngCore;
use crate::core::{Individual, OError};
use crate::operators::{BinaryComparisonOperator, PreferredSolution};
pub trait Selector {
fn select(
&self,
individuals: &[Individual],
number_of_winners: usize,
rng: &mut dyn RngCore,
) -> Result<Vec<Individual>, OError> {
let mut winners: Vec<Individual> = Vec::new();
for _ in 0..number_of_winners {
winners.push(self.select_fit_individual(individuals, rng)?);
}
Ok(winners)
}
fn select_fit_individual(
&self,
individuals: &[Individual],
rng: &mut dyn RngCore,
) -> Result<Individual, OError>;
}
pub struct TournamentSelector<Operator: BinaryComparisonOperator> {
number_of_competitors: usize,
_fitness_function: PhantomData<Operator>,
}
impl<Operator: BinaryComparisonOperator> TournamentSelector<Operator> {
pub fn new(number_of_competitors: usize) -> Self {
Self {
_fitness_function: PhantomData::<Operator>,
number_of_competitors,
}
}
}
impl<Operator: BinaryComparisonOperator> Selector for TournamentSelector<Operator> {
fn select_fit_individual(
&self,
individuals: &[Individual],
rng: &mut dyn RngCore,
) -> Result<Individual, OError> {
if individuals.is_empty() {
return Err(OError::SelectorOperator(
"BinaryComparisonOperator".to_string(),
"The population is empty and no individual can be selected".to_string(),
));
}
if individuals.len() < self.number_of_competitors {
return Err(OError::SelectorOperator(
"BinaryComparisonOperator".to_string(),
format!("The population size ({}) is smaller than the number of competitors needed in the tournament ({})", individuals.len(), self.number_of_competitors))
);
}
let mut winner = individuals.choose(rng).unwrap();
for _ in 0..self.number_of_competitors {
let potential_winner = individuals.choose(rng).unwrap();
let preferred_sol = Operator::compare(winner, potential_winner)?;
if preferred_sol == PreferredSolution::Second {
winner = potential_winner;
} else if preferred_sol == PreferredSolution::MutuallyPreferred {
winner = [winner, potential_winner].choose(rng).unwrap();
}
}
Ok(winner.clone())
}
}