use crate::traits::ChromosomeT;
use log::{debug, trace};
use rand::Rng;
pub fn boltzmann_selection<U: ChromosomeT>(
chromosomes: &[U],
couples: usize,
temperature: f64,
) -> Vec<(usize, usize)> {
debug!(target="selection_events", method="boltzmann"; "Starting Boltzmann selection");
let n = chromosomes.len();
if n < 2 {
debug!(target="selection_events", method="boltzmann"; "Population too small ({}), returning empty", n);
return Vec::new();
}
let temp = if temperature <= 0.0 {
debug!(target="selection_events", method="boltzmann"; "Temperature {} <= 0.0, using fallback 1.0", temperature);
1.0
} else {
temperature
};
let fitnesses: Vec<f64> = chromosomes.iter().map(|c| c.fitness()).collect();
let max_fitness = fitnesses.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let weights: Vec<f64> = fitnesses
.iter()
.map(|&f| ((f - max_fitness) / temp).exp())
.collect();
let total_weight: f64 = weights.iter().sum();
let mut cumulative = Vec::with_capacity(n);
let mut cum = 0.0;
for (i, &w) in weights.iter().enumerate() {
let prob = if total_weight > 0.0 {
w / total_weight
} else {
1.0 / n as f64
};
cum += prob;
cumulative.push(cum);
trace!(target="selection_events", method="boltzmann"; "Index {} fitness {} weight {} cum_prob {}", i, fitnesses[i], w, cum);
}
if let Some(last) = cumulative.last_mut() {
*last = 1.0;
}
let mut rng = crate::rng::make_rng();
let total_parents = couples * 2;
let mut selected = Vec::with_capacity(total_parents);
for _ in 0..total_parents {
let r: f64 = rng.random_range(0.0..1.0);
let idx = cumulative.iter().position(|&cp| cp >= r).unwrap_or(n - 1);
selected.push(idx);
}
let mut mating = Vec::new();
for chunk in selected.chunks(2) {
if chunk.len() == 2 {
mating.push((chunk[0], chunk[1]));
trace!(target="selection_events", method="boltzmann"; "Mating pair: {} - {}", chunk[0], chunk[1]);
}
}
debug!(target="selection_events", method="boltzmann"; "Boltzmann selection finished with {} pairs", mating.len());
mating
}