use crate::benchmarks::BenchmarkFn;
fn bounds_01(n: usize) -> Vec<(f64, f64)> {
vec![(0.0, 1.0); n]
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DTLZ1 {
n_vars: usize,
n_obj: usize,
bounds: Vec<(f64, f64)>,
}
impl DTLZ1 {
pub fn new(n_vars: usize, n_obj: usize) -> Self {
Self {
n_vars,
n_obj,
bounds: bounds_01(n_vars),
}
}
}
impl BenchmarkFn for DTLZ1 {
fn name(&self) -> &'static str {
"DTLZ1"
}
fn bounds(&self) -> &[(f64, f64)] {
&self.bounds
}
fn optimum_value(&self) -> Vec<f64> {
vec![0.0; self.n_obj]
}
fn evaluate(&self, x: &[f64]) -> Vec<f64> {
assert_eq!(
x.len(),
self.n_vars,
"DTLZ1::evaluate called with {} variables, expected {}",
x.len(),
self.n_vars
);
let k = self.n_vars - self.n_obj + 1;
let x_m = &x[self.n_obj - 1..];
let g: f64 = 100.0
* (k as f64
+ x_m
.iter()
.map(|&xi| {
(xi - 0.5).powi(2) - (20.0 * std::f64::consts::PI * (xi - 0.5)).cos()
})
.sum::<f64>());
let mut f = vec![0.0; self.n_obj];
let m = self.n_obj;
for i in 0..m {
let mut prod = 1.0;
for &xj in &x[..m - 1 - i] {
prod *= xj;
}
if i == 0 {
f[i] = 0.5 * prod * (1.0 + g);
} else {
f[i] = 0.5 * prod * (1.0 - x[m - 1 - i]) * (1.0 + g);
}
}
f
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DTLZ2 {
n_vars: usize,
n_obj: usize,
bounds: Vec<(f64, f64)>,
}
impl DTLZ2 {
pub fn new(n_vars: usize, n_obj: usize) -> Self {
Self {
n_vars,
n_obj,
bounds: bounds_01(n_vars),
}
}
}
impl BenchmarkFn for DTLZ2 {
fn name(&self) -> &'static str {
"DTLZ2"
}
fn bounds(&self) -> &[(f64, f64)] {
&self.bounds
}
fn optimum_value(&self) -> Vec<f64> {
vec![0.0; self.n_obj]
}
fn evaluate(&self, x: &[f64]) -> Vec<f64> {
assert_eq!(
x.len(),
self.n_vars,
"DTLZ2::evaluate called with {} variables, expected {}",
x.len(),
self.n_vars
);
self.evaluate_dtlz2_like(x)
}
}
impl DTLZ2 {
fn evaluate_dtlz2_like_core(x_pos: &[f64], g: f64, m: usize) -> Vec<f64> {
let mut f = vec![0.0; m];
for i in 0..m {
let mut prod = 1.0;
for &xj in &x_pos[..m - 1 - i] {
prod *= xj.cos();
}
if i == 0 {
f[i] = (1.0 + g) * prod;
} else {
f[i] = (1.0 + g) * prod * x_pos[m - 1 - i].sin();
}
}
f
}
fn evaluate_dtlz2_like(&self, x: &[f64]) -> Vec<f64> {
let _k = self.n_vars - self.n_obj + 1;
let x_m = &x[self.n_obj - 1..];
let g: f64 = x_m.iter().map(|&xi| (xi - 0.5).powi(2)).sum();
let x_pos: Vec<f64> = x[0..self.n_obj - 1]
.iter()
.map(|&xj| xj * std::f64::consts::FRAC_PI_2)
.collect();
Self::evaluate_dtlz2_like_core(&x_pos, g, self.n_obj)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DTLZ3 {
n_vars: usize,
n_obj: usize,
bounds: Vec<(f64, f64)>,
}
impl DTLZ3 {
pub fn new(n_vars: usize, n_obj: usize) -> Self {
Self {
n_vars,
n_obj,
bounds: bounds_01(n_vars),
}
}
}
impl BenchmarkFn for DTLZ3 {
fn name(&self) -> &'static str {
"DTLZ3"
}
fn bounds(&self) -> &[(f64, f64)] {
&self.bounds
}
fn optimum_value(&self) -> Vec<f64> {
vec![0.0; self.n_obj]
}
fn evaluate(&self, x: &[f64]) -> Vec<f64> {
assert_eq!(
x.len(),
self.n_vars,
"DTLZ3::evaluate called with {} variables, expected {}",
x.len(),
self.n_vars
);
let k = self.n_vars - self.n_obj + 1;
let x_m = &x[self.n_obj - 1..];
let g: f64 = 100.0
* (k as f64
+ x_m
.iter()
.map(|&xi| {
(xi - 0.5).powi(2) - (20.0 * std::f64::consts::PI * (xi - 0.5)).cos()
})
.sum::<f64>());
let x_pos: Vec<f64> = x[0..self.n_obj - 1]
.iter()
.map(|&xj| xj * std::f64::consts::FRAC_PI_2)
.collect();
DTLZ2::evaluate_dtlz2_like_core(&x_pos, g, self.n_obj)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DTLZ4 {
n_vars: usize,
n_obj: usize,
alpha: f64,
bounds: Vec<(f64, f64)>,
}
impl DTLZ4 {
pub fn new(n_vars: usize, n_obj: usize) -> Self {
Self {
n_vars,
n_obj,
alpha: 100.0,
bounds: bounds_01(n_vars),
}
}
pub fn with_alpha(n_vars: usize, n_obj: usize, alpha: f64) -> Self {
Self {
n_vars,
n_obj,
alpha,
bounds: bounds_01(n_vars),
}
}
}
impl BenchmarkFn for DTLZ4 {
fn name(&self) -> &'static str {
"DTLZ4"
}
fn bounds(&self) -> &[(f64, f64)] {
&self.bounds
}
fn optimum_value(&self) -> Vec<f64> {
vec![0.0; self.n_obj]
}
fn evaluate(&self, x: &[f64]) -> Vec<f64> {
assert_eq!(
x.len(),
self.n_vars,
"DTLZ4::evaluate called with {} variables, expected {}",
x.len(),
self.n_vars
);
let _k = self.n_vars - self.n_obj + 1;
let x_m = &x[self.n_obj - 1..];
let g: f64 = x_m.iter().map(|&xi| (xi - 0.5).powi(2)).sum();
let x_pos: Vec<f64> = x[0..self.n_obj - 1]
.iter()
.map(|&xj| xj.powf(self.alpha) * std::f64::consts::FRAC_PI_2)
.collect();
DTLZ2::evaluate_dtlz2_like_core(&x_pos, g, self.n_obj)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DTLZ5 {
n_vars: usize,
n_obj: usize,
bounds: Vec<(f64, f64)>,
}
impl DTLZ5 {
pub fn new(n_vars: usize, n_obj: usize) -> Self {
Self {
n_vars,
n_obj,
bounds: bounds_01(n_vars),
}
}
}
impl BenchmarkFn for DTLZ5 {
fn name(&self) -> &'static str {
"DTLZ5"
}
fn bounds(&self) -> &[(f64, f64)] {
&self.bounds
}
fn optimum_value(&self) -> Vec<f64> {
vec![0.0; self.n_obj]
}
fn evaluate(&self, x: &[f64]) -> Vec<f64> {
assert_eq!(
x.len(),
self.n_vars,
"DTLZ5::evaluate called with {} variables, expected {}",
x.len(),
self.n_vars
);
let _k = self.n_vars - self.n_obj + 1;
let x_m = &x[self.n_obj - 1..];
let g: f64 = x_m.iter().map(|&xi| (xi - 0.5).powi(2)).sum();
let m = self.n_obj;
let mut theta = Vec::with_capacity(m - 1);
for (i, &xi) in x[..m - 1].iter().enumerate() {
let t = if i == 0 {
xi * std::f64::consts::FRAC_PI_2
} else {
std::f64::consts::PI / (4.0 * (1.0 + g)) * (1.0 + 2.0 * g * xi)
};
theta.push(t);
}
DTLZ2::evaluate_dtlz2_like_core(&theta, g, self.n_obj)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DTLZ6 {
n_vars: usize,
n_obj: usize,
bounds: Vec<(f64, f64)>,
}
impl DTLZ6 {
pub fn new(n_vars: usize, n_obj: usize) -> Self {
Self {
n_vars,
n_obj,
bounds: bounds_01(n_vars),
}
}
}
impl BenchmarkFn for DTLZ6 {
fn name(&self) -> &'static str {
"DTLZ6"
}
fn bounds(&self) -> &[(f64, f64)] {
&self.bounds
}
fn optimum_value(&self) -> Vec<f64> {
vec![0.0; self.n_obj]
}
fn evaluate(&self, x: &[f64]) -> Vec<f64> {
assert_eq!(
x.len(),
self.n_vars,
"DTLZ6::evaluate called with {} variables, expected {}",
x.len(),
self.n_vars
);
let _k = self.n_vars - self.n_obj + 1;
let x_m = &x[self.n_obj - 1..];
let g: f64 = x_m.iter().map(|&xi| xi.powi(3)).sum::<f64>().sqrt();
let m = self.n_obj;
let mut theta = Vec::with_capacity(m - 1);
for (i, &xi) in x[..m - 1].iter().enumerate() {
let t = if i == 0 {
xi * std::f64::consts::FRAC_PI_2
} else {
std::f64::consts::PI / (4.0 * (1.0 + g)) * (1.0 + 2.0 * g * xi)
};
theta.push(t);
}
DTLZ2::evaluate_dtlz2_like_core(&theta, g, self.n_obj)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DTLZ7 {
n_vars: usize,
n_obj: usize,
bounds: Vec<(f64, f64)>,
}
impl DTLZ7 {
pub fn new(n_vars: usize, n_obj: usize) -> Self {
Self {
n_vars,
n_obj,
bounds: bounds_01(n_vars),
}
}
}
impl BenchmarkFn for DTLZ7 {
fn name(&self) -> &'static str {
"DTLZ7"
}
fn bounds(&self) -> &[(f64, f64)] {
&self.bounds
}
fn optimum_value(&self) -> Vec<f64> {
vec![0.0; self.n_obj]
}
fn evaluate(&self, x: &[f64]) -> Vec<f64> {
assert_eq!(
x.len(),
self.n_vars,
"DTLZ7::evaluate called with {} variables, expected {}",
x.len(),
self.n_vars
);
let k = self.n_vars - self.n_obj + 1;
let x_m = &x[self.n_obj - 1..];
let g: f64 = 1.0 + 9.0 * x_m.iter().sum::<f64>() / k as f64;
let m = self.n_obj;
let mut f = vec![0.0; m];
f[..m - 1].copy_from_slice(&x[..m - 1]);
let mut h_sum = 0.0;
for &fi in &f[..m - 1] {
let term = fi / (1.0 + g);
h_sum += term * (1.0 + (3.0 * std::f64::consts::PI * term).sin());
}
let h = m as f64 - h_sum;
f[m - 1] = (1.0 + g) * h;
f
}
}