radiate_core/
replacement.rs1use super::{Chromosome, Genotype, Population, random_provider};
2use crate::{Ecosystem, MetricSet, Phenotype, error::RadiateResult, metric_names};
3use radiate_error::radiate_bail;
4use radiate_expr::Expr;
5use radiate_utils::{AnyValue, DataType};
6use std::{collections::HashSet, sync::Arc};
7
8pub trait ReplacementStrategy<C: Chromosome>: Send + Sync {
16 fn replace(
17 &self,
18 population: &Population<C>,
19 encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
20 ) -> Genotype<C>;
21
22 fn replace_at(
23 &self,
24 _: usize,
25 population: &Population<C>,
26 encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
27 ) -> Genotype<C> {
28 self.replace(population, encoder)
29 }
30}
31
32pub struct EncodeReplace;
35
36impl<C: Chromosome> ReplacementStrategy<C> for EncodeReplace {
37 fn replace(
38 &self,
39 _: &Population<C>,
40 encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
41 ) -> Genotype<C> {
42 encoder()
43 }
44}
45
46pub struct PopulationSampleReplace;
51
52impl<C: Chromosome + Clone> ReplacementStrategy<C> for PopulationSampleReplace {
53 fn replace(
54 &self,
55 population: &Population<C>,
56 _: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
57 ) -> Genotype<C> {
58 let random_member = random_provider::range(0..population.len());
59 population[random_member].genotype().clone()
60 }
61}
62
63pub trait EcosystemFilter<C: Chromosome>: Send + Sync {
64 fn filter(
65 &mut self,
66 generation: usize,
67 ecosystem: &mut Ecosystem<C>,
68 metrics: &mut MetricSet,
69 replacer: Arc<dyn ReplacementStrategy<C>>,
70 encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
71 ) -> RadiateResult<()>;
72}
73
74pub struct UniqueScoreFilter {
75 filter_cond: Expr,
76}
77
78impl UniqueScoreFilter {
79 pub fn new(max_stagnation: usize, threshold: f32) -> Self {
80 Self {
81 filter_cond: Expr::select(metric_names::BEST_SCORES)
82 .stagnation(threshold)
83 .cast(DataType::Usize)
84 .gt(max_stagnation),
85 }
86 }
87}
88
89impl<C: Chromosome> EcosystemFilter<C> for UniqueScoreFilter {
90 fn filter(
91 &mut self,
92 generation: usize,
93 ecosystem: &mut Ecosystem<C>,
94 metrics: &mut MetricSet,
95 replacer: Arc<dyn ReplacementStrategy<C>>,
96 encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
97 ) -> RadiateResult<()> {
98 let should_filter = self.filter_cond.evaluate(metrics)?;
99
100 let AnyValue::Bool(bool) = should_filter else {
101 radiate_bail!(Engine: "Filter condition must evaluate to a boolean value, but got: {:?}", should_filter);
102 };
103
104 if !bool {
105 metrics.upsert(metric_names::FILTER_UNIQUE_SCORES, 0);
106 return Ok(());
107 }
108
109 let mut unique_scores = HashSet::new();
110 let mut replaced_count = 0;
111
112 for i in 0..ecosystem.population.len() {
113 if let Some(phenotype) = ecosystem.get_phenotype(i) {
114 let score = match phenotype.score() {
115 Some(score) => score.clone(),
116 None => continue,
117 };
118
119 if !unique_scores.insert(score) {
120 replaced_count += 1;
121 let new_genotype =
122 replacer.replace_at(i, ecosystem.population(), Arc::clone(&encoder));
123 if let Some(phenotype_mut) = ecosystem.get_phenotype_mut(i) {
124 *phenotype_mut = Phenotype::from((new_genotype, generation));
125 }
126 }
127 }
128 }
129
130 metrics.upsert(metric_names::FILTER_UNIQUE_SCORES, replaced_count);
131
132 Ok(())
133 }
134}