use crate::problem::{Bound, MultiProblem, Problem};
pub struct Sphere {
bounds: Vec<Bound>,
}
impl Sphere {
pub fn new(dim: usize) -> Self {
Sphere {
bounds: vec![(-5.12, 5.12); dim],
}
}
}
impl Problem for Sphere {
fn dim(&self) -> usize {
self.bounds.len()
}
fn bounds(&self) -> &[Bound] {
&self.bounds
}
fn objective(&self, x: &[f64]) -> f64 {
x.iter().map(|v| v * v).sum()
}
}
pub struct Rosenbrock {
bounds: Vec<Bound>,
}
impl Rosenbrock {
pub fn new(dim: usize) -> Self {
assert!(dim >= 2, "Rosenbrock needs at least 2 dimensions");
Rosenbrock {
bounds: vec![(-5.0, 10.0); dim],
}
}
}
impl Problem for Rosenbrock {
fn dim(&self) -> usize {
self.bounds.len()
}
fn bounds(&self) -> &[Bound] {
&self.bounds
}
fn objective(&self, x: &[f64]) -> f64 {
x.windows(2)
.map(|w| 100.0 * (w[1] - w[0] * w[0]).powi(2) + (1.0 - w[0]).powi(2))
.sum()
}
}
pub struct Rastrigin {
bounds: Vec<Bound>,
}
impl Rastrigin {
pub fn new(dim: usize) -> Self {
Rastrigin {
bounds: vec![(-5.12, 5.12); dim],
}
}
}
impl Problem for Rastrigin {
fn dim(&self) -> usize {
self.bounds.len()
}
fn bounds(&self) -> &[Bound] {
&self.bounds
}
fn objective(&self, x: &[f64]) -> f64 {
let n = x.len() as f64;
10.0 * n
+ x.iter()
.map(|v| v * v - 10.0 * (2.0 * std::f64::consts::PI * v).cos())
.sum::<f64>()
}
}
pub struct Zdt1 {
bounds: Vec<Bound>,
}
impl Zdt1 {
pub fn new(dim: usize) -> Self {
assert!(dim >= 2, "ZDT1 needs at least 2 dimensions");
Zdt1 {
bounds: vec![(0.0, 1.0); dim],
}
}
pub fn front_f2(f1: f64) -> f64 {
1.0 - f1.sqrt()
}
}
impl MultiProblem for Zdt1 {
fn dim(&self) -> usize {
self.bounds.len()
}
fn bounds(&self) -> &[Bound] {
&self.bounds
}
fn n_objectives(&self) -> usize {
2
}
fn objectives(&self, x: &[f64]) -> Vec<f64> {
let n = x.len();
let f1 = x[0];
let g = 1.0 + 9.0 * x[1..].iter().sum::<f64>() / (n - 1) as f64;
let f2 = g * (1.0 - (f1 / g).sqrt());
vec![f1, f2]
}
}
pub struct Dtlz2 {
bounds: Vec<Bound>,
m: usize,
}
impl Dtlz2 {
pub fn new(m: usize, dim: usize) -> Self {
assert!(m >= 2 && dim > m, "DTLZ2 needs m >= 2 and dim > m");
Dtlz2 {
bounds: vec![(0.0, 1.0); dim],
m,
}
}
}
impl MultiProblem for Dtlz2 {
fn dim(&self) -> usize {
self.bounds.len()
}
fn bounds(&self) -> &[Bound] {
&self.bounds
}
fn n_objectives(&self) -> usize {
self.m
}
fn objectives(&self, x: &[f64]) -> Vec<f64> {
let m = self.m;
let g: f64 = x[m - 1..].iter().map(|&xi| (xi - 0.5).powi(2)).sum();
let half_pi = std::f64::consts::FRAC_PI_2;
let mut f = vec![1.0 + g; m];
for (i, fi) in f.iter_mut().enumerate() {
for &xj in x.iter().take(m - 1 - i) {
*fi *= (xj * half_pi).cos();
}
if i > 0 {
*fi *= (x[m - 1 - i] * half_pi).sin();
}
}
f
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_minima_are_zero() {
assert_eq!(Sphere::new(3).objective(&[0.0, 0.0, 0.0]), 0.0);
assert!(Rosenbrock::new(3).objective(&[1.0, 1.0, 1.0]).abs() < 1e-12);
assert!(Rastrigin::new(4).objective(&[0.0; 4]).abs() < 1e-12);
}
#[test]
fn dtlz2_front_lies_on_the_unit_sphere() {
let p = Dtlz2::new(3, 12);
for angles in [[0.0, 0.0], [0.5, 0.5], [1.0, 0.3]] {
let mut x = vec![0.5; 12];
x[0] = angles[0];
x[1] = angles[1];
let f = p.objectives(&x);
let sumsq: f64 = f.iter().map(|v| v * v).sum();
assert!((sumsq - 1.0).abs() < 1e-9, "Σf² = {sumsq}");
}
}
#[test]
fn zdt1_pareto_set_lies_on_the_analytical_front() {
let p = Zdt1::new(5);
for &f1 in &[0.0, 0.25, 0.5, 1.0] {
let mut x = vec![0.0; 5];
x[0] = f1;
let o = p.objectives(&x);
assert!((o[0] - f1).abs() < 1e-12);
assert!((o[1] - Zdt1::front_f2(f1)).abs() < 1e-12);
}
}
}