use std::f64::consts::PI;
use heuropt::prelude::*;
struct Zdt1 {
dim: usize,
}
impl Problem for Zdt1 {
type Decision = Vec<f64>;
fn objectives(&self) -> ObjectiveSpace {
ObjectiveSpace::new(vec![Objective::minimize("f1"), Objective::minimize("f2")])
}
fn evaluate(&self, x: &Vec<f64>) -> Evaluation {
debug_assert_eq!(x.len(), self.dim);
let f1 = x[0];
let tail_sum: f64 = x[1..].iter().sum();
let g = 1.0 + 9.0 * tail_sum / (self.dim as f64 - 1.0);
let f2 = g * (1.0 - (f1 / g).sqrt());
Evaluation::new(vec![f1, f2])
}
}
struct Rastrigin {
dim: usize,
}
impl Problem for Rastrigin {
type Decision = Vec<f64>;
fn objectives(&self) -> ObjectiveSpace {
ObjectiveSpace::new(vec![Objective::minimize("f")])
}
fn evaluate(&self, x: &Vec<f64>) -> Evaluation {
let n = self.dim as f64;
let value = 10.0 * n
+ x.iter().map(|v| v * v - 10.0 * (2.0 * PI * v).cos()).sum::<f64>();
Evaluation::new(vec![value])
}
}
fn mean_distance_to_zdt1_front(front: &[Candidate<Vec<f64>>]) -> f64 {
let samples: Vec<(f64, f64)> = (0..=1000)
.map(|i| {
let f1 = i as f64 / 1000.0;
(f1, 1.0 - f1.sqrt())
})
.collect();
let mut total = 0.0;
for c in front {
let f1 = c.evaluation.objectives[0];
let f2 = c.evaluation.objectives[1];
let mut best = f64::INFINITY;
for &(rf1, rf2) in &samples {
let d = ((rf1 - f1).powi(2) + (rf2 - f2).powi(2)).sqrt();
if d < best {
best = d;
}
}
total += best;
}
total / front.len().max(1) as f64
}
fn run_zdt1() {
let dim = 30;
let problem = Zdt1 { dim };
let bounds = vec![(0.0, 1.0); dim];
let initializer = RealBounds::new(bounds.clone());
let variation = CompositeVariation {
crossover: SimulatedBinaryCrossover::new(bounds.clone(), 15.0, 0.5),
mutation: PolynomialMutation::new(bounds, 20.0, 1.0 / dim as f64),
};
let config = Nsga2Config { population_size: 100, generations: 1000, seed: 42 };
let mut optimizer = Nsga2::new(config, initializer, variation);
let result = optimizer.run(&problem);
let dist = mean_distance_to_zdt1_front(&result.pareto_front);
println!("== ZDT1 (NSGA-II, dim={dim}) ==");
println!(" population: {}", result.population.len());
println!(" pareto front: {}", result.pareto_front.len());
println!(" evaluations: {}", result.evaluations);
println!(" mean L2 to true front: {dist:.5}");
println!(
" example front point: f1={:.4}, f2={:.4}",
result.pareto_front[0].evaluation.objectives[0],
result.pareto_front[0].evaluation.objectives[1],
);
}
fn run_rastrigin() {
let dim = 5;
let problem = Rastrigin { dim };
let bounds = RealBounds::new(vec![(-5.12, 5.12); dim]);
let config = DifferentialEvolutionConfig {
population_size: 100,
generations: 1000,
differential_weight: 0.5,
crossover_probability: 0.9,
seed: 1,
};
let mut optimizer = DifferentialEvolution::new(config, bounds);
let result = optimizer.run(&problem);
let best = result.best.expect("single-objective always has a best");
println!("== Rastrigin (DE, dim={dim}) ==");
println!(" evaluations: {}", result.evaluations);
println!(" best f: {:.6e}", best.evaluation.objectives[0]);
println!(" best x: {:?}", best.decision);
}
fn main() {
run_zdt1();
println!();
run_rastrigin();
}