use std::sync::Arc;
use genetic_algorithms::chromosomes::Binary as BinaryChromosome;
use genetic_algorithms::configuration::ProblemSolving;
use genetic_algorithms::ga::{Ga, TerminationCause};
use genetic_algorithms::genotypes::Binary;
use genetic_algorithms::initializers::binary_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, SelectionConfig, StoppingConfig,
};
use genetic_algorithms::LogObserver;
fn main() {
const NUM_FEATURES: usize = 20;
const POP_SIZE: usize = 80;
const MAX_GENERATIONS: usize = 200;
const RELEVANT: &[usize] = &[0, 1, 2, 3];
let fitness_fn = |dna: &[Binary]| -> f64 {
let relevant_selected = RELEVANT.iter().filter(|&&i| dna[i].value).count() as f64;
let irrelevant_selected = dna
.iter()
.enumerate()
.filter(|(i, g)| !RELEVANT.contains(i) && g.value)
.count() as f64;
relevant_selected - 0.5 * irrelevant_selected
};
let mut ga = Ga::new()
.with_genes_per_chromosome(NUM_FEATURES)
.with_population_size(POP_SIZE)
.with_initialization_fn(binary_random_initialization)
.with_fitness_fn(fitness_fn)
.with_selection_method(Selection::Tournament)
.with_crossover_probability_max(0.9)
.with_crossover_probability_min(0.5)
.with_crossover_method(Crossover::Uniform)
.with_mutation_method(Mutation::BitFlip)
.with_survivor_method(Survivor::Fitness)
.with_problem_solving(ProblemSolving::Maximization)
.with_adaptive_ga(true)
.with_max_generations(MAX_GENERATIONS)
.with_observer(Arc::new(LogObserver))
.build()
.expect("Failed to build GA configuration");
println!("== Feature Selection with Adaptive GA ==");
println!(
"Features: {} (relevant: {:?}), Population: {}, Max generations: {}",
NUM_FEATURES, RELEVANT, POP_SIZE, MAX_GENERATIONS
);
println!("Operators: Selection=Tournament, Crossover=Uniform, Mutation=BitFlip");
println!("Adaptive GA: enabled (auto-adjusts crossover/mutation probabilities)");
println!("-------------------------------------------------------");
let report_interval = 25;
let result = ga.run_with_callback(
Some(
|gen: &usize,
pop: &Population<BinaryChromosome>,
_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 = {:6.2}, avg = {:6.2}",
gen, pop.best_chromosome.fitness, avg_fitness
);
std::ops::ControlFlow::Continue(())
},
),
report_interval,
);
match result {
Ok(population) => {
println!("-------------------------------------------------------");
println!(
"Finished. Best fitness: {:.2}",
population.best_chromosome.fitness
);
let mask: Vec<usize> = population
.best_chromosome
.dna()
.iter()
.enumerate()
.filter(|(_, g)| g.value)
.map(|(i, _)| i)
.collect();
println!("Selected features: {:?}", mask);
println!("Expected relevant features: {:?}", RELEVANT);
if RELEVANT.iter().all(|r| mask.contains(r)) {
println!("SUCCESS: All relevant features were selected!");
} else {
println!(
"Not all relevant features found. Try increasing generations or population size."
);
}
}
Err(e) => {
println!("GA failed: {:?}", e);
}
}
}