use genetic_algorithm::fitness::placeholders::CountTrue;
use genetic_algorithm::strategy::evolve::prelude::*;
use rand::Rng;
#[derive(Clone, Debug)]
pub struct MultiPointExtension {
pub selection_threshold: usize,
}
impl Extension for MultiPointExtension {
type Genotype = BinaryGenotype;
fn after_selection_complete<R: Rng, SR: StrategyReporter<Genotype = Self::Genotype>>(
&mut self,
genotype: &mut Self::Genotype,
state: &mut EvolveState<Self::Genotype>,
config: &EvolveConfig,
reporter: &mut SR,
_rng: &mut R,
) {
if let Some(cardinality) = state.population_cardinality() {
if cardinality <= self.selection_threshold {
println!(
"After selection: Low diversity detected ({}), applying mass extinction",
cardinality
);
reporter.on_extension_event(
ExtensionEvent("MassExtinctionAfterSelection".to_string()),
genotype,
state,
config,
);
let keep_size = (state.population.size() as f32 * 0.2).ceil() as usize;
let mut elite = self.extract_elite_chromosomes(genotype, state, config, keep_size);
state.population.truncate(2);
state.population.chromosomes.append(&mut elite);
}
}
}
fn after_crossover_complete<R: Rng, SR: StrategyReporter<Genotype = Self::Genotype>>(
&mut self,
_genotype: &mut Self::Genotype,
state: &mut EvolveState<Self::Genotype>,
_config: &EvolveConfig,
_reporter: &mut SR,
_rng: &mut R,
) {
let avg_age = state
.population
.chromosomes
.iter()
.map(|c| c.age())
.sum::<usize>() as f64
/ state.population.size() as f64;
println!(
"After crossover: Population size: {}, Avg age: {:.2}",
state.population.size(),
avg_age
);
}
fn after_generation_complete<R: Rng, SR: StrategyReporter<Genotype = Self::Genotype>>(
&mut self,
genotype: &mut Self::Genotype,
state: &mut EvolveState<Self::Genotype>,
config: &EvolveConfig,
reporter: &mut SR,
_rng: &mut R,
) {
if genotype.genes_hashing() {
let unique_count = state.population.unique_chromosome_indices().len();
let total_count = state.population.size();
let duplicate_ratio = 1.0 - (unique_count as f64 / total_count as f64);
if duplicate_ratio > 0.5 {
println!(
"After generation: High duplication ratio ({:.2}%), removing duplicates",
duplicate_ratio * 100.0
);
reporter.on_extension_event(
ExtensionEvent("RemoveDuplicates".to_string()),
genotype,
state,
config,
);
let mut unique = self.extract_unique_chromosomes(genotype, state, config);
let remaining = 2usize.saturating_sub(unique.len());
state.population.truncate(remaining);
state.population.chromosomes.append(&mut unique);
}
}
}
}
fn main() {
println!("Starting evolution with multiple extension points...\n");
let genotype = BinaryGenotype::builder()
.with_genes_size(50)
.with_genes_hashing(true) .with_chromosome_recycling(true)
.build()
.unwrap();
let evolve = Evolve::builder()
.with_genotype(genotype)
.with_select(SelectElite::new(0.9, 0.02))
.with_crossover(CrossoverUniform::new(0.8, 0.8))
.with_mutate(MutateSingleGene::new(0.1))
.with_fitness(CountTrue)
.with_fitness_ordering(FitnessOrdering::Maximize)
.with_extension(MultiPointExtension {
selection_threshold: 10,
})
.with_target_population_size(50)
.with_target_fitness_score(50) .with_max_stale_generations(200)
.with_max_generations(10_000)
.with_rng_seed_from_u64(42) .call()
.unwrap();
let (best_genes, best_fitness) = evolve.best_genes_and_fitness_score().unwrap();
let true_count = best_genes.iter().filter(|&&g| g).count();
println!("\n=== Evolution complete ===");
println!("Best fitness: {}", best_fitness);
println!("True genes: {}/{}", true_count, best_genes.len());
println!("Total generations: {}", evolve.state.current_generation);
println!("Best found at generation: {}", evolve.state.best_generation);
}