test(pareto,metrics,selection): pin shared-utility comparisons and arithmetic
Phase 1, tier 3 of the mutation-testing campaign — the shared Pareto / metric / selection utilities used by every multi-objective algorithm. A scoped cargo-mutants run found 75 survivors across these files; the tests below target them. - metrics/hypervolume.rs: dominates() boundary cases, non_dominated_ projection retained-set pins, hso_recursive 1-D/2-D base cases, hypervolume_nd_from_evaluations empty/non-dominating skips. - selection/tournament.rs: challenger_wins across the full feasibility cross-product + equal-objective tie; better_by_objective and better_by_feasibility branch pins; stochastic_ranking_select pf=0 feasibility ordering and count-wraps-modulo-population. - pareto/crowding.rs: exact interior crowding distance on symmetric and asymmetric fronts (pins the (next-prev)/span arithmetic). - pareto/sort.rs: three-non-dominated-then-one-dominated and a strict 3-chain producing three singleton fronts. - pareto/dominance.rs: trade-off → NonDominated, better-on-one-equal- on-other → Dominates, identical → Equal. - pareto/archive.rs: truncate boundary, trade-off kept alongside, equal candidate rejected, smaller-violation infeasible eviction. - pareto/front.rs: best_candidate keeps the first of tied minima. - metrics/spacing.rs: exact spacing for a varying-NN-distance front. src/core/problem.rs's lone survivor (decision_schema default body 'replace with vec![]') is an equivalent mutant — Vec::new() and vec![] are identical — and is left in the residue.
This commit is contained in:
@@ -160,4 +160,33 @@ mod tests {
|
||||
assert!(d[2].is_infinite());
|
||||
assert!(d[1].is_finite());
|
||||
}
|
||||
|
||||
/// Crowding distance pins the exact interior contribution: for a 3-point
|
||||
/// 2-objective front, the middle point's distance is the sum over both
|
||||
/// objectives of (next - prev) / span. With evenly-spaced points the
|
||||
/// value is exactly 2.0 (1.0 per objective).
|
||||
#[test]
|
||||
fn interior_point_distance_is_pinned() {
|
||||
let s = space_min2();
|
||||
// Front along the line f1 + f2 = 4: (0,4), (2,2), (4,0).
|
||||
let pop = [cand(vec![0.0, 4.0]), cand(vec![2.0, 2.0]), cand(vec![4.0, 0.0])];
|
||||
let d = crowding_distance(&pop, &[0, 1, 2], &s);
|
||||
// Boundary points are infinite; the middle point gets
|
||||
// (4-0)/4 + (4-0)/4 = 2.0 (objective 0 span 4, objective 1 span 4).
|
||||
assert!(d[0].is_infinite());
|
||||
assert!(d[2].is_infinite());
|
||||
assert!((d[1] - 2.0).abs() < 1e-12, "interior distance = {}", d[1]);
|
||||
}
|
||||
|
||||
/// An asymmetric front pins the per-objective `(next - prev) / span`
|
||||
/// arithmetic: catches the `-` ↔ `+`/`/` and `/` ↔ `*` mutants.
|
||||
#[test]
|
||||
fn asymmetric_interior_distance_is_pinned() {
|
||||
let s = space_min2();
|
||||
// (0,10), (1,2), (10,0): objective-0 span = 10, objective-1 span = 10.
|
||||
let pop = [cand(vec![0.0, 10.0]), cand(vec![1.0, 2.0]), cand(vec![10.0, 0.0])];
|
||||
let d = crowding_distance(&pop, &[0, 1, 2], &s);
|
||||
// middle point: obj0 (10-0)/10 = 1.0; obj1 (10-0)/10 = 1.0 → 2.0.
|
||||
assert!((d[1] - 2.0).abs() < 1e-12, "got {}", d[1]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user