use genetic_algorithms::chromosomes::Range as RangeChromosome;
use genetic_algorithms::genotypes::Range as RangeGenotype;
use genetic_algorithms::initializers::range_random_initialization;
use genetic_algorithms::prelude::*;
use genetic_algorithms::ga::TerminationCause;
use genetic_algorithms::operations::GaussianParams;
use genetic_algorithms::population::Population;
use genetic_algorithms::stats::GenerationStats;
#[cfg(feature = "observer-metrics")]
use genetic_algorithms::MetricsObserver;
use genetic_algorithms::{rng, CompositeObserver};
use std::sync::Arc;
fn main() {
let _ = env_logger::try_init();
let args: Vec<String> = std::env::args().collect();
let seed_opt: Option<u64> = args
.iter()
.position(|a| a == "--seed")
.and_then(|pos| args.get(pos + 1))
.and_then(|v| v.parse::<u64>().ok());
if let Some(s) = seed_opt {
#[cfg(all(not(target_arch = "wasm32"), feature = "parallel"))]
{
rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build_global()
.ok(); }
rng::set_seed(Some(s));
}
const DIMENSIONS: usize = 5;
const POP_SIZE: usize = 100;
const MAX_GENERATIONS: usize = 500;
let fitness_fn = |dna: &[RangeGenotype<f64>]| -> f64 {
let a = 10.0;
let n = dna.len() as f64;
a * n
+ dna
.iter()
.map(|g| g.value.powi(2) - a * (2.0 * std::f64::consts::PI * g.value).cos())
.sum::<f64>()
};
let alleles = vec![RangeGenotype::new(0, vec![(-5.12, 5.12)], 0.0_f64)];
let alleles_clone = alleles.clone();
let composite = CompositeObserver::new().register(Arc::new(LogObserver));
#[cfg(feature = "observer-metrics")]
let composite = composite.register(Arc::new(MetricsObserver::new("rastrigin")));
let mut ga_builder = Ga::new()
.with_chromosome_length(ChromosomeLength::Fixed(DIMENSIONS))
.with_population_size(POP_SIZE)
.with_initialization_fn(move |genes_per_chromosome, _| {
range_random_initialization(genes_per_chromosome, Some(&alleles_clone))
})
.with_fitness_fn(fitness_fn)
.with_selection_method(Selection::Tournament)
.with_crossover_method(Crossover::Uniform)
.with_mutation_method(Mutation::Gaussian(GaussianParams { sigma: None }))
.with_survivor_method(Survivor::Fitness)
.with_problem_solving(ProblemSolving::Minimization)
.with_max_generations(MAX_GENERATIONS)
.with_observer(Arc::new(composite));
if let Some(s) = seed_opt {
ga_builder = ga_builder.with_rng_seed(s);
}
let mut ga = ga_builder
.build()
.expect("Failed to build GA configuration");
println!("== Rastrigin Continuous Optimization ==");
println!(
"Chromosome: {} dimensions, Population: {}, Max generations: {}",
DIMENSIONS, POP_SIZE, MAX_GENERATIONS
);
println!("Operators: Selection=Tournament, Crossover=Uniform, Mutation=Gaussian");
println!("-------------------------------------------------------");
let report_interval = 50;
#[cfg(feature = "visualization")]
let plot_stats: std::sync::Arc<
std::sync::Mutex<Vec<genetic_algorithms::stats::GenerationStats>>,
> = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
#[cfg(feature = "visualization")]
let plot_stats_clone = plot_stats.clone();
let result = ga.run_with_callback(
Some(
move |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 = {:8.4}, avg = {:8.4}",
gen, pop.best_chromosome.fitness, avg_fitness
);
#[cfg(feature = "visualization")]
if let Ok(mut guard) = plot_stats_clone.lock() {
guard.push(_stats.clone());
}
std::ops::ControlFlow::Continue(())
},
),
report_interval,
);
match result {
Ok(population) => {
println!("-------------------------------------------------------");
println!(
"Finished. Best fitness: {:.6}",
population.best_chromosome.fitness
);
if population.best_chromosome.fitness < 1.0 {
println!("Near-optimal solution found!");
} else {
println!("Did not reach optimum. Try increasing generations or population size.");
}
#[cfg(feature = "visualization")]
if std::env::args().any(|a| a == "--plot") {
let stats_guard = plot_stats.lock().unwrap_or_else(|e| e.into_inner());
let stats = &*stats_guard;
std::fs::create_dir_all("docs/images").expect("failed to create docs/images");
genetic_algorithms::visualization::plot_fitness(stats, "docs/images/rastrigin.png")
.expect("plot failed");
println!("Fitness plot saved to docs/images/rastrigin.png");
}
}
Err(e) => {
println!("GA failed: {:?}", e);
}
}
}