perf(pareto): precompute oriented buffers in pareto_front
`pareto_front` was still the naive O(n²) formulation: a raw double loop calling `pareto_compare` for every ordered pair, re-deriving feasibility and the minimization-oriented objective values on every comparison. Apply the same precompute pattern `non_dominated_sort` already uses: hoist per-individual `feasible` / `violation` / `oriented` (a flat n*m buffer) out of the loop, then run a branchless inlined dominance check over the contiguous buffer. The kept set and its order are unchanged. Whole-program callgrind Ir for the `compare_profile` benchmark: 221,836,742,708 -> 173,803,642,945 (-21.65%); `pareto_front` self-Ir 92.1B -> 44.1B.
This commit is contained in:
+55
-8
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
use crate::core::candidate::Candidate;
|
use crate::core::candidate::Candidate;
|
||||||
use crate::core::objective::ObjectiveSpace;
|
use crate::core::objective::ObjectiveSpace;
|
||||||
use crate::pareto::dominance::{Dominance, pareto_compare};
|
|
||||||
|
|
||||||
/// Return all candidates that are not dominated by any other candidate.
|
/// Return all candidates that are not dominated by any other candidate.
|
||||||
///
|
///
|
||||||
@@ -31,20 +30,68 @@ pub fn pareto_front<D: Clone>(
|
|||||||
population: &[Candidate<D>],
|
population: &[Candidate<D>],
|
||||||
objectives: &ObjectiveSpace,
|
objectives: &ObjectiveSpace,
|
||||||
) -> Vec<Candidate<D>> {
|
) -> Vec<Candidate<D>> {
|
||||||
|
let n = population.len();
|
||||||
|
if n == 0 {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precompute per-individual feasibility, violation, and the
|
||||||
|
// minimization-oriented objective vectors once, mirroring
|
||||||
|
// `non_dominated_sort`. The naïve formulation called `pareto_compare`
|
||||||
|
// (and therefore `as_minimization`) for every ordered pair, re-deriving
|
||||||
|
// all of this on every comparison; precomputing turns the O(n²) inner
|
||||||
|
// loop into a branchless scan over a contiguous buffer.
|
||||||
|
let feasible: Vec<bool> = population
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.evaluation.is_feasible())
|
||||||
|
.collect();
|
||||||
|
let violation: Vec<f64> = population
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.evaluation.constraint_violation)
|
||||||
|
.collect();
|
||||||
|
let m = objectives.len();
|
||||||
|
let mut oriented: Vec<f64> = Vec::with_capacity(n * m);
|
||||||
|
for c in population {
|
||||||
|
oriented.extend_from_slice(&objectives.as_minimization(&c.evaluation.objectives));
|
||||||
|
}
|
||||||
|
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
'outer: for (i, a) in population.iter().enumerate() {
|
'outer: for i in 0..n {
|
||||||
for (j, b) in population.iter().enumerate() {
|
let ai_feasible = feasible[i];
|
||||||
|
let ai_violation = violation[i];
|
||||||
|
let ai = &oriented[i * m..i * m + m];
|
||||||
|
for j in 0..n {
|
||||||
if i == j {
|
if i == j {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if matches!(
|
// `i` is kept only if no `j` dominates it — i.e. no `j` for which
|
||||||
pareto_compare(&a.evaluation, &b.evaluation, objectives),
|
// `pareto_compare(a_i, a_j)` would be `DominatedBy`. This inlines
|
||||||
Dominance::DominatedBy
|
// exactly that one outcome of `pareto_compare`.
|
||||||
) {
|
let dominated_by_j = match (ai_feasible, feasible[j]) {
|
||||||
|
(true, false) => false,
|
||||||
|
(false, true) => true,
|
||||||
|
(false, false) => ai_violation > violation[j],
|
||||||
|
(true, true) => {
|
||||||
|
let aj = &oriented[j * m..j * m + m];
|
||||||
|
let mut a_better_anywhere = false;
|
||||||
|
let mut b_better_anywhere = false;
|
||||||
|
for k in 0..m {
|
||||||
|
let av = ai[k];
|
||||||
|
let bv = aj[k];
|
||||||
|
if av < bv {
|
||||||
|
a_better_anywhere = true;
|
||||||
|
} else if av > bv {
|
||||||
|
b_better_anywhere = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b_better_anywhere && !a_better_anywhere
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if dominated_by_j {
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.push(a.clone());
|
out.push(population[i].clone());
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user