use super::{Chromosome, Genotype, Population, random_provider};
use crate::{Ecosystem, MetricSet, Phenotype, error::RadiateResult, metric_names};
use radiate_error::radiate_bail;
use radiate_expr::Expr;
use radiate_utils::{AnyValue, DataType};
use std::{collections::HashSet, sync::Arc};
pub trait ReplacementStrategy<C: Chromosome>: Send + Sync {
fn replace(
&self,
population: &Population<C>,
encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
) -> Genotype<C>;
fn replace_at(
&self,
_: usize,
population: &Population<C>,
encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
) -> Genotype<C> {
self.replace(population, encoder)
}
}
pub struct EncodeReplace;
impl<C: Chromosome> ReplacementStrategy<C> for EncodeReplace {
fn replace(
&self,
_: &Population<C>,
encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
) -> Genotype<C> {
encoder()
}
}
pub struct PopulationSampleReplace;
impl<C: Chromosome + Clone> ReplacementStrategy<C> for PopulationSampleReplace {
fn replace(
&self,
population: &Population<C>,
_: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
) -> Genotype<C> {
let random_member = random_provider::range(0..population.len());
population[random_member].genotype().clone()
}
}
pub trait EcosystemFilter<C: Chromosome>: Send + Sync {
fn filter(
&mut self,
generation: usize,
ecosystem: &mut Ecosystem<C>,
metrics: &mut MetricSet,
replacer: Arc<dyn ReplacementStrategy<C>>,
encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
) -> RadiateResult<()>;
}
pub struct UniqueScoreFilter {
filter_cond: Expr,
}
impl UniqueScoreFilter {
pub fn new(max_stagnation: usize, threshold: f32) -> Self {
Self {
filter_cond: Expr::select(metric_names::BEST_SCORES)
.stagnation(threshold)
.cast(DataType::Usize)
.gt(max_stagnation),
}
}
}
impl<C: Chromosome> EcosystemFilter<C> for UniqueScoreFilter {
fn filter(
&mut self,
generation: usize,
ecosystem: &mut Ecosystem<C>,
metrics: &mut MetricSet,
replacer: Arc<dyn ReplacementStrategy<C>>,
encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
) -> RadiateResult<()> {
let should_filter = self.filter_cond.evaluate(metrics)?;
let AnyValue::Bool(bool) = should_filter else {
radiate_bail!(Engine: "Filter condition must evaluate to a boolean value, but got: {:?}", should_filter);
};
if !bool {
metrics.upsert(metric_names::FILTER_UNIQUE_SCORES, 0);
return Ok(());
}
let mut unique_scores = HashSet::new();
let mut replaced_count = 0;
for i in 0..ecosystem.population.len() {
if let Some(phenotype) = ecosystem.get_phenotype(i) {
let score = match phenotype.score() {
Some(score) => score.clone(),
None => continue,
};
if !unique_scores.insert(score) {
replaced_count += 1;
let new_genotype =
replacer.replace_at(i, ecosystem.population(), Arc::clone(&encoder));
if let Some(phenotype_mut) = ecosystem.get_phenotype_mut(i) {
*phenotype_mut = Phenotype::from((new_genotype, generation));
}
}
}
}
metrics.upsert(metric_names::FILTER_UNIQUE_SCORES, replaced_count);
Ok(())
}
}