test(genetic_algorithm): pin compare_for_fitness and survival_selection

Phase 1 tests for GA — feasibility-first fitness comparison across all
branches, and survival_selection's exact elite + best-offspring
composition (including the zero-elitism case).
This commit is contained in:
2026-05-13 22:58:17 -06:00
parent 4569244a68
commit 03b450f050
+68
View File
@@ -415,4 +415,72 @@ mod tests {
);
let _ = opt.run(&Sphere1D);
}
// ---- Mutation-test pinned helpers --------------------------------------
use crate::core::candidate::Candidate;
use crate::core::evaluation::Evaluation;
fn fc(obj: f64) -> Candidate<u32> {
Candidate::new(0, Evaluation::new(vec![obj]))
}
fn fc_cv(obj: f64, cv: f64) -> Candidate<u32> {
Candidate::new(0, Evaluation::constrained(vec![obj], cv))
}
#[test]
fn compare_for_fitness_feasibility_first() {
let feasible = fc(100.0);
let infeasible = fc_cv(0.0, 1.0);
assert_eq!(
compare_for_fitness(&feasible, &infeasible, Direction::Minimize),
std::cmp::Ordering::Less,
);
assert_eq!(
compare_for_fitness(&infeasible, &feasible, Direction::Minimize),
std::cmp::Ordering::Greater,
);
}
#[test]
fn compare_for_fitness_two_feasible_min_and_max() {
let lo = fc(1.0);
let hi = fc(2.0);
assert_eq!(compare_for_fitness(&lo, &hi, Direction::Minimize), std::cmp::Ordering::Less);
assert_eq!(compare_for_fitness(&lo, &hi, Direction::Maximize), std::cmp::Ordering::Greater);
}
#[test]
fn compare_for_fitness_two_infeasible_lower_violation_wins() {
let low = fc_cv(0.0, 0.3);
let high = fc_cv(0.0, 0.9);
assert_eq!(compare_for_fitness(&low, &high, Direction::Minimize), std::cmp::Ordering::Less);
}
/// `survival_selection` carries `elitism` parents and `n - elitism`
/// offspring, each set sorted best-first. Pin the exact composition.
#[test]
fn survival_selection_keeps_elites_and_best_offspring() {
// Parents: objectives 5, 1, 9 → best is 1.
let parents = vec![fc(5.0), fc(1.0), fc(9.0)];
// Offspring: objectives 4, 2, 8 → best two are 2, 4.
let offspring = vec![fc(4.0), fc(2.0), fc(8.0)];
let next = survival_selection(&parents, offspring, Direction::Minimize, 3, 1);
assert_eq!(next.len(), 3);
// 1 elite (best parent = 1.0) + 2 best offspring (2.0, 4.0).
assert_eq!(next[0].evaluation.objectives[0], 1.0);
assert_eq!(next[1].evaluation.objectives[0], 2.0);
assert_eq!(next[2].evaluation.objectives[0], 4.0);
}
#[test]
fn survival_selection_zero_elitism_is_all_offspring() {
let parents = vec![fc(1.0)];
let offspring = vec![fc(9.0), fc(3.0)];
let next = survival_selection(&parents, offspring, Direction::Minimize, 2, 0);
assert_eq!(next.len(), 2);
// No elites — both slots come from offspring, best-first.
assert_eq!(next[0].evaluation.objectives[0], 3.0);
assert_eq!(next[1].evaluation.objectives[0], 9.0);
}
}