use super::{clamp, sample, Evaluator, Optimizer};
use crate::problem::Problem;
use crate::rng::Rng;
use crate::solution::{Report, Solution};
use crate::termination::Termination;
#[derive(Debug, Clone, Copy)]
pub struct Pso {
pub swarm_size: usize,
pub inertia: f64,
pub cognitive: f64,
pub social: f64,
pub seed: u64,
}
impl Default for Pso {
fn default() -> Self {
Pso {
swarm_size: 30,
inertia: 0.7298,
cognitive: 1.49618,
social: 1.49618,
seed: 42,
}
}
}
impl Optimizer for Pso {
fn with_seed(&self, seed: u64) -> Self {
Pso { seed, ..*self }
}
fn optimize(&self, problem: &dyn Problem, term: &Termination) -> Report {
let bounds = problem.bounds();
let dim = bounds.len();
let n = self.swarm_size.max(2);
let mut rng = Rng::new(self.seed);
let vmax: Vec<f64> = bounds.iter().map(|&(lo, hi)| hi - lo).collect();
let first_x = sample(bounds, &mut rng);
let first_v = finite_or_worst(problem.objective(&first_x));
let mut ev = Evaluator::new(
problem,
term,
Solution {
x: first_x.clone(),
value: first_v,
},
);
let mut pos: Vec<Vec<f64>> = Vec::with_capacity(n);
let mut vel: Vec<Vec<f64>> = Vec::with_capacity(n);
let mut pbest_x: Vec<Vec<f64>> = Vec::with_capacity(n);
let mut pbest_f: Vec<f64> = Vec::with_capacity(n);
vel.push(init_velocity(&first_x, bounds, &mut rng));
pbest_x.push(first_x.clone());
pbest_f.push(first_v);
pos.push(first_x);
for _ in 1..n {
let x = sample(bounds, &mut rng);
let v = init_velocity(&x, bounds, &mut rng);
let f = if ev.done() {
f64::INFINITY
} else {
finite_or_worst(ev.eval(&x))
};
pbest_x.push(x.clone());
pbest_f.push(f);
pos.push(x);
vel.push(v);
}
while !ev.done() {
for i in 0..n {
if ev.done() {
break;
}
let gbest = ev.best.x.clone();
for d in 0..dim {
let (lo, hi) = bounds[d];
let r1 = rng.uniform();
let r2 = rng.uniform();
let mut v = self.inertia * vel[i][d]
+ self.cognitive * r1 * (pbest_x[i][d] - pos[i][d])
+ self.social * r2 * (gbest[d] - pos[i][d]);
v = clamp(v, -vmax[d], vmax[d]);
let mut x = pos[i][d] + v;
if x < lo {
x = lo;
v = 0.0;
} else if x > hi {
x = hi;
v = 0.0;
}
vel[i][d] = v;
pos[i][d] = x;
}
let f = finite_or_worst(ev.eval(&pos[i]));
if f < pbest_f[i] {
pbest_f[i] = f;
pbest_x[i].copy_from_slice(&pos[i]);
}
}
}
ev.finish()
}
}
#[inline]
fn finite_or_worst(v: f64) -> f64 {
if v.is_finite() {
v
} else {
f64::INFINITY
}
}
fn init_velocity(x: &[f64], bounds: &[(f64, f64)], rng: &mut Rng) -> Vec<f64> {
bounds
.iter()
.zip(x)
.map(|(&(lo, hi), &xi)| 0.5 * (rng.uniform_in(lo, hi) - xi))
.collect()
}