use rand::{Rng, RngCore};
use crate::traits::{UpdateContext, Velocity};
#[derive(Debug, Clone)]
pub struct FipsVelocity {
pub phi: f64,
chi: f64,
}
impl FipsVelocity {
pub fn new(phi: f64) -> Self {
assert!(
phi > 4.0,
"FIPS uses constriction: requires φ > 4 (got {phi})"
);
let chi = 2.0 / (2.0 - phi - (phi * phi - 4.0 * phi).sqrt()).abs();
Self { phi, chi }
}
pub fn chi(&self) -> f64 {
self.chi
}
}
impl Default for FipsVelocity {
fn default() -> Self {
Self::new(4.1)
}
}
impl Velocity for FipsVelocity {
fn update(&self, ctx: &UpdateContext, rng: &mut dyn RngCore) -> Vec<f64> {
let dim = ctx.position.len();
let k = ctx.neighbor_bests.len().max(1);
let phi_k = self.phi / k as f64;
let mut new_v = Vec::with_capacity(dim);
for d in 0..dim {
let mut social = 0.0;
for nb in ctx.neighbor_bests {
let r: f64 = rng.gen();
social += phi_k * r * (nb[d] - ctx.position[d]);
}
new_v.push(self.chi * (ctx.velocity[d] + social));
}
new_v
}
fn needs_full_neighborhood(&self) -> bool {
true
}
}