swarmkit
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 ;
// `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 = ;
let mut searcher = new
.bounded_by // your Boundary impl
.into_gbest_searcher; // your FitCalc impl
let mut group = my_init.init;
let best = searcher.iter.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 }orVonNeumann); 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;
mover.into_lbest_searcher;
mover.into_niched_searcher;
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 whatBestit sees each step.BoundedMover— wraps a mover and applies aBoundaryafter each update. Build viamover.bounded_by(b).ChainedMover— runs two movers in sequence on the same particle, sharing the RNG. Build viam1.chain(m2).AdapterMover— lifts a mover written against a sub-unit (e.g. just the XY of aPath<N>) onto the parent unit. Build viamover.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 = new // custom mover (tangent-frame update)
.chain // sequence a second mover
. // lift sub-unit mover onto parent unit
.bounded_by; // clamp after each step
let time = time_searcher.nested; // an inner PSO, every outer step
let searcher = space.chain // outer + inner movers in sequence
.into_gbest_searcher; // 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
- Apache License, Version 2.0 (
LICENSE-APACHEor http://www.apache.org/licenses/LICENSE-2.0) - MIT license (
LICENSE-MITor http://opensource.org/licenses/MIT)
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.