use crate::core::candidate::Candidate;
use crate::core::objective::ObjectiveSpace;
pub fn r2<D>(
approximation: &[Candidate<D>],
weights: &[Vec<f64>],
objectives: &ObjectiveSpace,
) -> f64 {
assert!(
!approximation.is_empty(),
"r2: approximation must not be empty"
);
assert!(!weights.is_empty(), "r2: weight set must not be empty");
let m = objectives.len();
for (i, w) in weights.iter().enumerate() {
assert_eq!(
w.len(),
m,
"r2: weight {i} has wrong length ({} vs {m})",
w.len()
);
assert!(
w.iter().all(|&v| v >= 0.0),
"r2: weight {i} has a negative entry"
);
assert!(w.iter().sum::<f64>() > 0.0, "r2: weight {i} has zero sum");
}
let oriented: Vec<Vec<f64>> = approximation
.iter()
.map(|c| objectives.as_minimization(&c.evaluation.objectives))
.collect();
let mut z_star = vec![f64::INFINITY; m];
for o in &oriented {
for k in 0..m {
if o[k] < z_star[k] {
z_star[k] = o[k];
}
}
}
let mut total = 0.0_f64;
for w in weights {
let mut best = f64::INFINITY;
for o in &oriented {
let mut t = 0.0_f64;
for k in 0..m {
let dk = (o[k] - z_star[k]).abs() * w[k];
if dk > t {
t = dk;
}
}
if t < best {
best = t;
}
}
total += best;
}
total / weights.len() as f64
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::evaluation::Evaluation;
use crate::core::objective::Objective;
use crate::pareto::das_dennis;
fn space_min2() -> ObjectiveSpace {
ObjectiveSpace::new(vec![Objective::minimize("f1"), Objective::minimize("f2")])
}
fn cand(obj: Vec<f64>) -> Candidate<()> {
Candidate::new((), Evaluation::new(obj))
}
#[test]
fn r2_extremes_are_perfect_at_endpoints() {
let s = space_min2();
let front = [cand(vec![0.0, 1.0]), cand(vec![1.0, 0.0])];
let weights = [vec![1.0, 0.0], vec![0.0, 1.0]];
assert!(r2(&front, &weights, &s) < 1e-12);
}
#[test]
fn r2_dense_dasdennis_finite_for_uniform_front() {
let s = space_min2();
let weights = das_dennis(2, 5);
let front: Vec<Candidate<()>> = (0..=10)
.map(|i| {
let t = i as f64 / 10.0;
cand(vec![t, 1.0 - t])
})
.collect();
let v = r2(&front, &weights, &s);
assert!(v.is_finite());
assert!(v >= 0.0);
}
#[test]
#[should_panic(expected = "approximation must not be empty")]
fn r2_empty_approximation_panics() {
let s = space_min2();
let weights = vec![vec![1.0, 0.0]];
let _: f64 = r2::<()>(&[], &weights, &s);
}
#[test]
#[should_panic(expected = "weight set must not be empty")]
fn r2_empty_weights_panics() {
let s = space_min2();
let front = [cand(vec![0.0, 1.0])];
let _ = r2(&front, &[], &s);
}
#[test]
#[should_panic(expected = "wrong length")]
fn r2_wrong_dim_weight_panics() {
let s = space_min2();
let front = [cand(vec![0.0, 1.0])];
let weights = vec![vec![1.0, 0.0, 0.0]];
let _ = r2(&front, &weights, &s);
}
}