perf(pareto): halve non_dominated_sort dominance comparisons (2.46M -> 1.27M instr)

The dominance relation is antisymmetric, so the outcome of compare(i, j)
fully determines compare(j, i). Iterating only j > i and applying the
result in both directions does identical work in half the pair scans.

non_dominated_sort_2d n=200: 2_461_178 -> 1_268_372 (-48%); n=50 -1.65x.
Ripples into dependents: nsga2 one-generation -20%, nsga3 / sms_emoa ~-9%.
Output is bit-identical (dominates[] still ascending, first_front order
unchanged) -- all 606 tests including run() snapshots pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 06:26:08 -06:00
co-authored by Claude Opus 4.7
parent b1d339a869
commit eb4c8a6a9f
+20 -8
View File
@@ -62,14 +62,15 @@ pub fn non_dominated_sort<D>(
let mut fronts: Vec<Vec<usize>> = Vec::new(); let mut fronts: Vec<Vec<usize>> = Vec::new();
let mut first_front: Vec<usize> = Vec::new(); let mut first_front: Vec<usize> = Vec::new();
// Compare each unordered pair {i, j} exactly once. The dominance
// relation is antisymmetric — the outcome of `compare(i, j)` fully
// determines `compare(j, i)` — so iterating `j > i` and applying the
// result in both directions does identical work in half the iterations.
for i in 0..n { for i in 0..n {
let ai_feasible = feasible[i]; let ai_feasible = feasible[i];
let ai_violation = violation[i]; let ai_violation = violation[i];
let ai = &oriented[i]; let ai = &oriented[i];
for j in 0..n { for j in (i + 1)..n {
if i == j {
continue;
}
let bi_feasible = feasible[j]; let bi_feasible = feasible[j];
let bi_violation = violation[j]; let bi_violation = violation[j];
// Inline the body of `pareto_compare`. We only care about // Inline the body of `pareto_compare`. We only care about
@@ -77,7 +78,7 @@ pub fn non_dominated_sort<D>(
// are no-ops here. // are no-ops here.
let dominates_outcome = match (ai_feasible, bi_feasible) { let dominates_outcome = match (ai_feasible, bi_feasible) {
(true, false) => Some(true), // i dominates j (true, false) => Some(true), // i dominates j
(false, true) => Some(false), // i is dominated (false, true) => Some(false), // j dominates i
(false, false) => { (false, false) => {
if ai_violation < bi_violation { if ai_violation < bi_violation {
Some(true) Some(true)
@@ -108,12 +109,23 @@ pub fn non_dominated_sort<D>(
} }
}; };
match dominates_outcome { match dominates_outcome {
Some(true) => dominates[i].push(j), Some(true) => {
Some(false) => dominated_by_count[i] += 1, // i dominates j
dominates[i].push(j);
dominated_by_count[j] += 1;
}
Some(false) => {
// j dominates i
dominates[j].push(i);
dominated_by_count[i] += 1;
}
None => {} None => {}
} }
} }
if dominated_by_count[i] == 0 { }
for (i, &count) in dominated_by_count.iter().enumerate() {
if count == 0 {
first_front.push(i); first_front.push(i);
} }
} }