From eb4c8a6a9fb146235ce52b07d6bdb86fd7529286 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Thu, 14 May 2026 06:24:30 -0600 Subject: [PATCH] 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) --- src/pareto/sort.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/pareto/sort.rs b/src/pareto/sort.rs index 703438a..a3dd576 100644 --- a/src/pareto/sort.rs +++ b/src/pareto/sort.rs @@ -62,14 +62,15 @@ pub fn non_dominated_sort( let mut fronts: Vec> = Vec::new(); let mut first_front: Vec = 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 { let ai_feasible = feasible[i]; let ai_violation = violation[i]; let ai = &oriented[i]; - for j in 0..n { - if i == j { - continue; - } + for j in (i + 1)..n { let bi_feasible = feasible[j]; let bi_violation = violation[j]; // Inline the body of `pareto_compare`. We only care about @@ -77,7 +78,7 @@ pub fn non_dominated_sort( // are no-ops here. let dominates_outcome = match (ai_feasible, bi_feasible) { (true, false) => Some(true), // i dominates j - (false, true) => Some(false), // i is dominated + (false, true) => Some(false), // j dominates i (false, false) => { if ai_violation < bi_violation { Some(true) @@ -108,12 +109,23 @@ pub fn non_dominated_sort( } }; match dominates_outcome { - Some(true) => dominates[i].push(j), - Some(false) => dominated_by_count[i] += 1, + Some(true) => { + // 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 => {} } } - if dominated_by_count[i] == 0 { + } + + for (i, &count) in dominated_by_count.iter().enumerate() { + if count == 0 { first_front.push(i); } }