swarmkit 0.1.0

Composable particle swarm optimization with nested searches
Documentation
//! [`Searcher`] trait and its [`SearcherIter`] adapter.

use crate::{Best, Contextful, Evolution, Group, NestedMover, ParticleInitDependent, SetTo, Unit};
use rand::Rng;

/// Drives one PSO step over a swarm.
///
/// Concrete impls — [`crate::GBestSearcher`], [`crate::LBestSearcher`],
/// [`crate::NichedSearcher`] — differ in what their movers see as the
/// social attractor and how per-particle context flows, but share this
/// interface.
pub trait Searcher: Contextful {
    /// The particle unit this searcher iterates over.
    type TUnit: Unit;

    /// Initial fitness pass + per-particle state seed.
    fn init(&mut self, particles: &mut Group<Self::TUnit>);

    /// Advance the swarm one step; return the swarm-wide best so far.
    fn next(&mut self, particles: &mut Group<Self::TUnit>) -> &Best<Self::TUnit>;

    /// Reseed the searcher's RNG.
    fn reseed<R: Rng>(&mut self, rng: &mut R);

    /// Wrap this searcher in a [`NestedMover`].
    #[must_use]
    fn nested<TInit>(
        self,
        max_iteration: usize,
        particle_init: TInit,
    ) -> NestedMover<<Self::TUnit as SetTo>::TTarget, Self::TUnit, Self, TInit>
    where
        Self: Sized,
        Self::TUnit: SetTo,
        TInit: ParticleInitDependent<TIn = <Self::TUnit as SetTo>::TTarget, TOut = Self::TUnit>,
    {
        NestedMover::new(max_iteration, self, particle_init)
    }

    /// Drive the searcher as an `Iterator<Item = Best<TUnit>>` for
    /// `max_iteration` steps; optionally record each step's [`Group`]
    /// snapshot into `evolution`.
    fn iter<'a>(
        &'a mut self,
        max_iteration: usize,
        group: &'a mut Group<Self::TUnit>,
        evolution: Option<&'a mut Evolution<Self::TUnit>>,
    ) -> SearcherIter<'a, Self::TUnit, Self>
    where
        Self: Sized,
    {
        SearcherIter::new(max_iteration, self, group, evolution)
    }
}

/// `Iterator<Item = Best<TUnit>>` over a [`Searcher`].
pub struct SearcherIter<'a, TUnit, TSearcher>
where
    TUnit: Unit,
    TSearcher: Searcher<TUnit = TUnit>,
{
    max_iteration: usize,
    iteration: usize,
    searcher: &'a mut TSearcher,
    group: &'a mut Group<TUnit>,
    evolution: Option<&'a mut Evolution<TUnit>>,
}

impl<'a, TUnit, TSearcher> SearcherIter<'a, TUnit, TSearcher>
where
    TUnit: Unit,
    TSearcher: Searcher<TUnit = TUnit>,
{
    /// Calls `searcher.init(group)` immediately so pbests are seeded
    /// before the first `next()`.
    pub fn new(
        max_iteration: usize,
        searcher: &'a mut TSearcher,
        group: &'a mut Group<TUnit>,
        evolution: Option<&'a mut Evolution<TUnit>>,
    ) -> Self {
        searcher.init(group);
        SearcherIter {
            max_iteration,
            iteration: 0,
            searcher,
            group,
            evolution,
        }
    }
}

impl<TUnit: Unit, TSearcher: Searcher<TUnit = TUnit>> Iterator
    for SearcherIter<'_, TUnit, TSearcher>
{
    type Item = Best<TUnit>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.iteration >= self.max_iteration {
            return None;
        }

        self.iteration += 1;
        self.searcher
            .set_iteration(self.iteration, self.max_iteration);
        let r = self.searcher.next(self.group);

        if let Some(e) = &mut self.evolution {
            e.push_snapshot(self.group);
        }

        Some(*r)
    }
}