swarmkit 0.1.0

Composable particle swarm optimization with nested searches
Documentation
  • Coverage
  • 100%
    100 out of 100 items documented1 out of 1 items with examples
  • Size
  • Source code size: 122.68 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 3.16 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 37s Average build duration of successful builds.
  • all releases: 37s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • Anvoker

swarmkit

Crates.io docs.rs

Composable Particle Swarm Optimization for Rust.

"Kit" because it's a framework for building PSO driven solutions, as opposed to a batteries included approach.

Full API: docs.rs/swarmkit.

Quick start

use swarmkit::{Floats, GBestMover, IntoGBestSearcher, ParticleInit, PSOCoeffs, Searcher};

// `Floats<N>` ships with `Add`/`Sub`/`Mul<f64>`/`FieldwiseClamp` and `Index`
// pre-implemented — drop it in as your particle and skip the per-type boilerplate.
type Vec2 = Floats<2>;

let mut searcher = GBestMover::<Vec2>::new(PSOCoeffs::default())
    .bounded_by(my_boundary)         // your Boundary impl
    .into_gbest_searcher(my_fit);    // your FitCalc impl

let mut group = my_init.init(&mut rng);
let best = searcher.iter(80, &mut group, None).last().unwrap();

Full runnable version at examples/elliptical.rs — run it with cargo run --example elliptical.

What you implement

Three traits, regardless of topology:

  • FitCalc — score a candidate; higher is better.
  • Boundary — what to do when a particle leaves the search space (typically clamp or reflect).
  • ParticleInit — seed initial positions and velocities.

For nested PSO, also ParticleInitDependent — same shape as ParticleInit, but seeds the inner swarm around an outer particle's position.

Particle type

Any Copy + Default + Debug + Send + Sync + 'static type qualifies (the Unit marker is auto-implemented for the bound). The PSO mover additionally needs Add, Sub, Mul<f64>; bounded movers need FieldwiseClamp.

  • Fixed-size numeric vectors: Floats<N> has all four pre-implemented. type MyUnit = Floats<10>; and go.
  • Compound types (e.g. waypoints + times, with sub-unit projection via ParticleRefFrom/SetTo): implement the four ops on your own newtype.

Available topologies

  • GBest — one swarm-wide attractor.
  • LBest — per-neighbourhood attractors (Ring { k } or VonNeumann); preserves diversity on multi-basin landscapes.
  • Niched — static multi-swarm partition; no coordination across niches.

The same mover plugs into all three — switching topology is a one-method-call change:

mover.into_gbest_searcher(fit);
mover.into_lbest_searcher(fit, LBestKind::Ring { k: 1 });
mover.into_niched_searcher(fit, partition);

Searchers and movers

A ParticleMover mutates one particle's position and velocity per iteration. A Searcher owns a mover, evaluates fitness, computes whichever per-iteration Best its topology defines, and feeds it to the mover. You drive the search by calling searcher.iter(max_iterations, &mut group, evolution) and consuming the resulting iterator.

  • GBestMover — the standard PSO velocity update. Works inside any of the three topologies; the searcher decides what Best it sees each step.
  • BoundedMover — wraps a mover and applies a Boundary after each update. Build via mover.bounded_by(b).
  • ChainedMover — runs two movers in sequence on the same particle, sharing the RNG. Build via m1.chain(m2).
  • AdapterMover — lifts a mover written against a sub-unit (e.g. just the XY of a Path<N>) onto the parent unit. Build via mover.adapt::<ParentCommon>().
  • NestedMover — runs an entire inner PSO search inside one outer mover step. Used for nested PSO.

All the combinators stack into a single fluent expression. Pared down from swarmkit-sailing — an outer spatial PSO with a nested per-segment timing PSO:

let space = SphericalPSOMover::<N, Path<N>>::new(coeffs)   // custom mover (tangent-frame update)
    .chain(CauchyKickMover::new(gamma))                    // sequence a second mover
    .adapt::<Best<Path<N>>>()                              // lift sub-unit mover onto parent unit
    .bounded_by(SailingBoundary::new(/* ... */));          // clamp after each step

let time = time_searcher.nested(8, TimeInit);              // an inner PSO, every outer step

let searcher = space.chain(time)                           // outer + inner movers in sequence
    .into_gbest_searcher(fit);                             // pick a topology

A larger example

swarmkit-sailing (in the bywind repo) is a substantive use — sailboat route optimization on a spherical Earth. It showcases nested PSO (an outer spatial PSO over route waypoints with a nested per-segment timing PSO inside each outer step) and how to wire a non-trivial physical model into swarmkit.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.