swarmkit 0.1.0

Composable particle swarm optimization with nested searches
Documentation
//! Core traits: [`Unit`], [`FitCalc`], [`Boundary`], [`Contextful`],
//! [`ParticleRefFrom`], [`SetTo`].

use crate::ParticleRefMut;
use std::fmt::Debug;

/// Borrow a sub-unit's [`ParticleRefMut`] out of a parent unit's view.
///
/// Lets a mover written against sub-unit `Self` operate on a particle whose
/// unit is `Self::TSource`, without copying. Pair with [`SetTo`] to write
/// the inner result back.
pub trait ParticleRefFrom {
    /// The parent unit.
    type TSource: Unit;

    /// The returned view aliases the same memory as `source`.
    fn divide_from<'a>(
        source: &'a mut ParticleRefMut<'_, Self::TSource>,
    ) -> ParticleRefMut<'a, Self>
    where
        Self: Copy;
}

/// Write a sub-unit's value back into a parent unit (inverse of
/// [`ParticleRefFrom`]).
pub trait SetTo {
    /// The parent unit.
    type TTarget: Unit;

    /// Write `self` into `target`.
    fn set_to_ref_mut(&self, target: &mut ParticleRefMut<'_, Self::TTarget>);
}

/// Reparameterizable per outer-step or per-iteration.
///
/// All movers, fit calcs, and boundaries implement it. Outer searchers
/// push their per-particle context into inner components via
/// [`Self::set_context`]; [`crate::Searcher`] pushes iteration progress
/// via [`Self::set_iteration`].
pub trait Contextful {
    /// Reparameterization payload.
    type TContext: Copy;

    /// Replace the current context. Defaults to a no-op; override when the
    /// impl actually reads `TContext`.
    fn set_context(&mut self, _context: Self::TContext) {}

    /// `iteration` is 1-based; the init (fitness-only) pass runs before
    /// any call. Override for iteration-dependent schedules.
    fn set_iteration(&mut self, _iteration: usize, _max_iteration: usize) {}
}

/// Fitness function. Higher return value is better.
pub trait FitCalc: Contextful + Sync {
    /// The unit being scored.
    type T: Unit;

    /// Parallel fitness pass stops splitting at this size. Default
    /// `usize::MAX` runs serially.
    const PAR_LEAF_SIZE: usize = usize::MAX;

    /// Higher is better.
    fn calculate_fit(&self, position: Self::T) -> f64;
}

/// Marker for the position/velocity space of a particle.
///
/// Bounds are the minimum needed for AoS storage, iteration logging, and
/// rayon's parallel passes. Mover-specific bounds (arithmetic,
/// [`crate::FieldwiseClamp`], `PartialOrd`) sit on the consumer.
///
/// Auto-implemented for every type that satisfies the bounds — users
/// don't write `impl Unit for X {}`.
pub trait Unit: Copy + Default + Debug + Send + Sync + 'static {}

impl<T: Copy + Default + Debug + Send + Sync + 'static> Unit for T {}

/// Map an out-of-bounds position back into the search domain.
///
/// Applied by [`crate::BoundedMover`] after each mover step. Implementors
/// typically clamp or reflect.
pub trait Boundary: Contextful + Sync {
    /// The unit being constrained.
    type T: Unit;

    /// Return a position inside the domain.
    fn handle(&self, pos: Self::T) -> Self::T;
}

/// Per-field analogue of [`f64::clamp`] (rectangular box constraint).
pub trait FieldwiseClamp {
    /// Per-field clamp into `[min, max]`.
    fn clamp(&self, min: Self, max: Self) -> Self;
}