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:
+20
-8
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user