use std::sync::Arc;
use genetic_algorithms::chromosomes::Range as RangeChromosome;
use genetic_algorithms::configuration::ProblemSolving;
use genetic_algorithms::ga::{Ga, TerminationCause};
use genetic_algorithms::genotypes::Range as RangeGenotype;
use genetic_algorithms::initializers::range_random_initialization;
use genetic_algorithms::operations::{Crossover, Mutation, Selection, Survivor};
use genetic_algorithms::population::Population;
use genetic_algorithms::stats::GenerationStats;
use genetic_algorithms::traits::{
ChromosomeT, ConfigurationT, CrossoverConfig, MutationConfig, NichingConfig, SelectionConfig,
StoppingConfig,
};
use genetic_algorithms::LogObserver;
fn main() {
const POP_SIZE: usize = 150;
const MAX_GENERATIONS: usize = 300;
const SIGMA_SHARE: f64 = 1.5;
const ALPHA: f64 = 1.0;
let fitness_fn = |dna: &[RangeGenotype<f64>]| -> f64 {
let x = dna[0].value;
let peak = |center: f64, height: f64| -> f64 {
height * (-((x - center).powi(2)) / (2.0 * 0.5_f64.powi(2))).exp()
};
peak(2.0, 1.0) + peak(5.0, 0.9) + peak(8.0, 0.8)
};
let alleles = vec![RangeGenotype::new(0_i32, vec![(0.0, 10.0)], 0.0_f64)];
let alleles_clone = alleles.clone();
let mut ga = Ga::new()
.with_genes_per_chromosome(1)
.with_population_size(POP_SIZE)
.with_initialization_fn(move |genes_per_chromosome, _, _| {
range_random_initialization(genes_per_chromosome, Some(&alleles_clone), Some(false))
})
.with_fitness_fn(fitness_fn)
.with_selection_method(Selection::Tournament)
.with_crossover_method(Crossover::Uniform)
.with_mutation_method(Mutation::Gaussian)
.with_survivor_method(Survivor::Fitness)
.with_problem_solving(ProblemSolving::Maximization)
.with_niching_enabled(true)
.with_niching_sigma_share(SIGMA_SHARE)
.with_niching_alpha(ALPHA)
.with_max_generations(MAX_GENERATIONS)
.with_observer(Arc::new(LogObserver))
.build()
.expect("Failed to build GA configuration");
println!("== Niching / Fitness Sharing Example ==");
println!("Landscape: 3 Gaussian peaks at x=2, x=5, x=8 in [0, 10]");
println!("Population: {POP_SIZE}, Max generations: {MAX_GENERATIONS}");
println!("Operators: Selection=Tournament, Crossover=Uniform, Mutation=Gaussian");
println!("Niching: sigma_share={SIGMA_SHARE}, alpha={ALPHA}");
println!("-------------------------------------------------------");
let report_interval = 50;
let result = ga.run_with_callback(
Some(
|gen: &usize,
pop: &Population<RangeChromosome<f64>>,
_stats: &GenerationStats,
_cause: &TerminationCause|
-> std::ops::ControlFlow<()> {
let avg_fitness =
pop.chromosomes.iter().map(|c| c.fitness()).sum::<f64>() / pop.size() as f64;
println!(
"Generation {:4}: best = {:.4}, avg = {:.4}",
gen, pop.best_chromosome.fitness, avg_fitness
);
std::ops::ControlFlow::Continue(())
},
),
report_interval,
);
match result {
Ok(population) => {
println!("-------------------------------------------------------");
println!(
"Finished. Best fitness: {:.4}",
population.best_chromosome.fitness
);
println!("\nTop solutions (showing population spread across peaks):");
let mut positions: Vec<f64> = population
.chromosomes
.iter()
.map(|c| c.dna()[0].value)
.collect();
positions.sort_by(|a, b| a.partial_cmp(b).unwrap());
let peaks = [
(2.0, "Peak 1 (x=2)"),
(5.0, "Peak 2 (x=5)"),
(8.0, "Peak 3 (x=8)"),
];
for (center, label) in &peaks {
let count = positions
.iter()
.filter(|&&x| (x - center).abs() < SIGMA_SHARE)
.count();
println!(" {label}: {count} individuals");
}
let peaks_found = peaks
.iter()
.filter(|(center, _)| {
positions.iter().any(|&x| (x - center).abs() < SIGMA_SHARE)
})
.count();
if peaks_found >= 3 {
println!("SUCCESS: Population covers all {peaks_found} peaks!");
} else {
println!(
"Found {peaks_found} of 3 peaks. Try increasing population or adjusting sigma_share."
);
}
}
Err(e) => {
println!("GA failed: {:?}", e);
}
}
}