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:
@@ -265,4 +265,62 @@ mod tests {
|
||||
a.extend(vec![cand(1, vec![1.0, 4.0]), cand(2, vec![3.0, 2.0])]);
|
||||
assert_eq!(a.members().len(), 2);
|
||||
}
|
||||
|
||||
/// `truncate` keeps the archive untouched when it is already at or
|
||||
/// below `max_size`, and trims it when over. Pins the `>` boundary.
|
||||
#[test]
|
||||
fn truncate_boundary_behavior() {
|
||||
let mut a = ParetoArchive::<u32>::new(space_min2());
|
||||
// Three mutually non-dominated members.
|
||||
a.insert(cand(1, vec![1.0, 3.0]));
|
||||
a.insert(cand(2, vec![2.0, 2.0]));
|
||||
a.insert(cand(3, vec![3.0, 1.0]));
|
||||
assert_eq!(a.members().len(), 3);
|
||||
// max_size == len → no-op (kills `>` → `>=`).
|
||||
a.truncate(3);
|
||||
assert_eq!(a.members().len(), 3);
|
||||
// max_size > len → no-op.
|
||||
a.truncate(10);
|
||||
assert_eq!(a.members().len(), 3);
|
||||
// max_size < len → trims.
|
||||
a.truncate(2);
|
||||
assert_eq!(a.members().len(), 2);
|
||||
}
|
||||
|
||||
/// A trade-off candidate (better on one axis, worse on the other) is
|
||||
/// neither dominated nor dominating — it must be *added* alongside the
|
||||
/// existing member. Pins the per-axis `<` / `>` scan in both
|
||||
/// `member_dominates_or_equals` and `candidate_dominates_member`.
|
||||
#[test]
|
||||
fn trade_off_candidate_is_kept_alongside() {
|
||||
let mut a = ParetoArchive::<u32>::new(space_min2());
|
||||
a.insert(cand(1, vec![1.0, 5.0]));
|
||||
a.insert(cand(2, vec![5.0, 1.0])); // trade-off — must be kept
|
||||
assert_eq!(a.members().len(), 2);
|
||||
}
|
||||
|
||||
/// An equal-objectives candidate is rejected (a member dominates-or-
|
||||
/// equals it). Pins the Equal branch — distinguishes `<=` from `<` in
|
||||
/// `candidate_dominates_member` and the `<=` in
|
||||
/// `member_dominates_or_equals`'s infeasible branch.
|
||||
#[test]
|
||||
fn equal_candidate_is_rejected() {
|
||||
let mut a = ParetoArchive::<u32>::new(space_min2());
|
||||
a.insert(cand(1, vec![2.0, 2.0]));
|
||||
a.insert(cand(2, vec![2.0, 2.0])); // identical objectives → rejected
|
||||
assert_eq!(a.members().len(), 1);
|
||||
assert_eq!(a.members()[0].decision, 1);
|
||||
}
|
||||
|
||||
/// Two infeasible candidates: the one with smaller constraint violation
|
||||
/// wins. Pins the `<` / `<=` in the infeasible branches.
|
||||
#[test]
|
||||
fn infeasible_candidate_with_smaller_violation_evicts_larger() {
|
||||
let mut a = ParetoArchive::<u32>::new(space_min2());
|
||||
a.insert(Candidate::new(1u32, Evaluation::constrained(vec![0.0, 0.0], 1.0)));
|
||||
// Smaller violation → dominates the existing infeasible member.
|
||||
a.insert(Candidate::new(2u32, Evaluation::constrained(vec![9.0, 9.0], 0.5)));
|
||||
assert_eq!(a.members().len(), 1);
|
||||
assert_eq!(a.members()[0].decision, 2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user