use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use genetic_algorithms::chromosomes::Binary as BinaryChromosome;
use genetic_algorithms::configuration::{GaConfiguration, ProblemSolving};
use genetic_algorithms::ga::Ga;
use genetic_algorithms::genotypes::Binary as BinaryGene;
use genetic_algorithms::initializers::binary_random_initialization;
use genetic_algorithms::island::configuration::IslandConfiguration;
use genetic_algorithms::island::IslandGa;
use genetic_algorithms::nsga2::configuration::Nsga2Configuration;
use genetic_algorithms::nsga2::Nsga2Ga;
use genetic_algorithms::observer::{GaObserver, IslandGaObserver, Nsga2Observer};
use genetic_algorithms::operations::{Crossover, Mutation, Selection, Survivor};
use genetic_algorithms::traits::{ChromosomeT, ConfigurationT, CrossoverConfig, MutationConfig, SelectionConfig, StoppingConfig};
use genetic_algorithms::observer::LogObserver;
use genetic_algorithms::{AllObserver, CompositeObserver};
#[derive(Default)]
struct CountingAllObserver {
ga_hooks: AtomicUsize,
island_hooks: AtomicUsize,
nsga2_hooks: AtomicUsize,
}
impl GaObserver<BinaryChromosome> for CountingAllObserver {
fn on_run_start(&self) {
self.ga_hooks.fetch_add(1, Ordering::Relaxed);
}
}
impl IslandGaObserver<BinaryChromosome> for CountingAllObserver {
fn on_island_run_start(&self, _island_id: usize) {
self.island_hooks.fetch_add(1, Ordering::Relaxed);
}
}
impl Nsga2Observer<BinaryChromosome> for CountingAllObserver {
fn on_pareto_front_assigned(&self, _generation: usize, _front_count: usize, _population_size: usize) {
self.nsga2_hooks.fetch_add(1, Ordering::Relaxed);
}
}
fn build_ga(
observer: Arc<dyn GaObserver<BinaryChromosome> + Send + Sync>,
max_generations: usize,
) -> Ga<BinaryChromosome> {
Ga::new()
.with_genes_per_chromosome(8)
.with_population_size(20)
.with_initialization_fn(binary_random_initialization)
.with_fitness_fn(|dna: &[BinaryGene]| dna.iter().filter(|g| g.value).count() as f64)
.with_selection_method(Selection::Tournament)
.with_crossover_method(Crossover::Uniform)
.with_mutation_method(Mutation::BitFlip)
.with_problem_solving(ProblemSolving::Maximization)
.with_survivor_method(Survivor::Fitness)
.with_max_generations(max_generations)
.with_observer(observer)
.build()
.unwrap()
}
#[test]
fn test_composite_observer_ga_hooks() {
let a = Arc::new(CountingAllObserver::default());
let b = Arc::new(CountingAllObserver::default());
let composite = CompositeObserver::<BinaryChromosome>::new()
.add(Arc::clone(&a) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>)
.add(Arc::clone(&b) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>);
let mut ga = build_ga(Arc::new(composite), 5);
ga.run().expect("GA run should succeed");
assert!(
a.ga_hooks.load(Ordering::Relaxed) >= 1,
"observer a should receive on_run_start"
);
assert!(
b.ga_hooks.load(Ordering::Relaxed) >= 1,
"observer b should receive on_run_start"
);
}
#[test]
fn test_composite_observer_island_hooks() {
let a = Arc::new(CountingAllObserver::default());
let b = Arc::new(CountingAllObserver::default());
let composite = CompositeObserver::<BinaryChromosome>::new()
.add(Arc::clone(&a) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>)
.add(Arc::clone(&b) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>);
let island_config = IslandConfiguration::new()
.with_num_islands(2)
.with_migration_interval(3)
.with_migration_count(1);
let ga_config = GaConfiguration::new()
.with_population_size(10)
.with_genes_per_chromosome(8)
.with_max_generations(5)
.with_selection_method(Selection::Tournament)
.with_crossover_method(Crossover::Uniform)
.with_mutation_method(Mutation::BitFlip)
.with_survivor_method(Survivor::Fitness)
.with_problem_solving(ProblemSolving::Maximization);
let mut island_ga = IslandGa::<BinaryChromosome>::new(island_config, ga_config)
.with_initialization_fn(binary_random_initialization)
.with_fitness_fn(|dna: &[BinaryGene]| dna.iter().filter(|g| g.value).count() as f64)
.with_observer(Arc::new(composite) as Arc<dyn IslandGaObserver<BinaryChromosome> + Send + Sync>)
.build()
.expect("IslandGa configuration should be valid");
island_ga.run().expect("IslandGa run should succeed");
assert!(
a.island_hooks.load(Ordering::Relaxed) > 0,
"observer a should receive on_island_run_start"
);
assert!(
b.island_hooks.load(Ordering::Relaxed) > 0,
"observer b should receive on_island_run_start"
);
}
#[test]
fn test_composite_observer_nsga2_hooks() {
let a = Arc::new(CountingAllObserver::default());
let b = Arc::new(CountingAllObserver::default());
let composite = CompositeObserver::<BinaryChromosome>::new()
.add(Arc::clone(&a) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>)
.add(Arc::clone(&b) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>);
let nsga2_config = Nsga2Configuration::new()
.with_num_objectives(2)
.with_population_size(10)
.with_max_generations(5);
let ga_config = GaConfiguration::default()
.with_crossover_method(Crossover::Uniform)
.with_mutation_method(Mutation::BitFlip);
let mut nsga2 = Nsga2Ga::<BinaryChromosome>::new(nsga2_config, ga_config)
.with_initialization_fn(binary_random_initialization)
.with_objective_fns(vec![
Box::new(|dna: &[BinaryGene]| dna.iter().filter(|g| g.value).count() as f64),
Box::new(|dna: &[BinaryGene]| dna.iter().filter(|g| !g.value).count() as f64),
])
.with_observer(Arc::new(composite) as Arc<dyn Nsga2Observer<BinaryChromosome> + Send + Sync>);
nsga2.run().expect("Nsga2Ga run should succeed");
assert!(
a.nsga2_hooks.load(Ordering::Relaxed) > 0,
"observer a should receive on_pareto_front_assigned"
);
assert!(
b.nsga2_hooks.load(Ordering::Relaxed) > 0,
"observer b should receive on_pareto_front_assigned"
);
}
#[test]
fn test_all_observer_bounds() {
fn assert_all_observer<U: ChromosomeT, T: AllObserver<U>>() {}
assert_all_observer::<BinaryChromosome, CompositeObserver<BinaryChromosome>>();
}
#[test]
fn test_composite_fan_out_order() {
let observer = Arc::new(CountingAllObserver::default());
let arc1 = Arc::clone(&observer) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>;
let arc2 = Arc::clone(&observer) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>;
let composite = CompositeObserver::<BinaryChromosome>::new()
.add(arc1)
.add(arc2);
let mut ga = build_ga(Arc::new(composite), 1);
ga.run().expect("GA run should succeed");
assert_eq!(
observer.ga_hooks.load(Ordering::Relaxed),
2,
"on_run_start should fire exactly 2 times (once per registered observer)"
);
}
#[test]
fn composite_observer_new_is_empty() {
let composite: CompositeObserver<BinaryChromosome> = CompositeObserver::new();
assert_eq!(composite.observer_count(), 0);
}
#[test]
fn composite_observer_add_builds_chain() {
let composite: CompositeObserver<BinaryChromosome> = CompositeObserver::new()
.add(Arc::new(LogObserver) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>)
.add(Arc::new(LogObserver) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>);
assert_eq!(composite.observer_count(), 2);
}
#[test]
fn composite_observer_default_is_empty() {
let composite: CompositeObserver<BinaryChromosome> = CompositeObserver::default();
assert_eq!(composite.observer_count(), 0);
}
#[test]
fn composite_observer_clone_shares_arcs() {
let composite: CompositeObserver<BinaryChromosome> = CompositeObserver::new()
.add(Arc::new(LogObserver) as Arc<dyn AllObserver<BinaryChromosome> + Send + Sync>);
let cloned = composite.clone();
assert_eq!(cloned.observer_count(), 1);
}