| Crates.io | swarm |
| lib.rs | swarm |
| version | 0.1.3 |
| created_at | 2025-07-08 16:34:53.280472+00 |
| updated_at | 2025-07-09 17:00:49.444591+00 |
| description | Fast blackbox optimisation tool. |
| homepage | |
| repository | https://github.com/alexlovric/swarm |
| max_upload_size | |
| id | 1743160 |
| size | 80,770 |
Black-box optimisation tool written in Rust. Clean API for solving complex single-objective and multi-objective optimisation problems using powerful metaheuristic algorithms. Swarm has the following features:
To use Swarm in your project, add it as a dependency in your Cargo.toml:
[dependencies]
swarm = "0.1.2" # (replace with current version)
This by default includes the parallel feature. This allows the use of solve_par which is very useful for computationally expensive objective functions. To disable it (for more lightweight build, or if parallel not necessary) use default-features = false.
pip install swarm_py
All functions provided to Swarm must be of the signature:
FnMut(&[f64]) -> (Vec<f64>, Option<Vec<f64>>)
Fn + Sync + Send).Problem: Minimise f(x, y) = (x² + y - 11)² + (x + y² - 7)²
use swarm::{error::Result, pso::PsoParams, Optimiser, Variable};
// Define the function (can also use closure)
fn himmelblau(x: &[f64]) -> (Vec<f64>, Option<Vec<f64>>) {
let f = (x[0].powi(2) + x[1] - 11.0).powi(2) + (x[0] + x[1].powi(2) - 7.0).powi(2);
(vec![f], None)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Define the search space for the variables
let vars = vec![Variable(-5.0, 5.0), Variable(-5.0, 5.0)];
// 2. Configure the optimiser
let optimiser = Optimiser::Pso {
n_particles: 100,
params: PsoParams::default(),
constraint_handler: None,
seed: None,
};
// 3. Find the minimum
let max_iter = 200;
let result = optimiser.solve(&mut himmelblau, &vars, max_iter)?;
// Note: 1. You can also call the pso function directly.
// Note: 2. You can pass a closure directly.
// Note: 3. For parallel execution use `solve_par`
// (if feature `parallel` is enabled)
// 4. Get the best solution found
let best = &result.solutions[0];
println!("Min at x = {:.4?}, f(x) = {:.4}", best.x, best.f[0]);
Ok(())
}
This example solves the constrained Binh and Korn problem. NSGA-II is ideal for this as it finds a set of trade-off solutions, known as the Pareto Front, rather than a single point.
Problem: Minimise two objectives, f1(x, y) and f2(x, y), subject to two constraints.
use swarm::{Optimiser, Variable, SbxParams, PmParams};
// Define the problem (can also use closure)
fn binh_and_korn(x: &[f64]) -> (Vec<f64>, Option<Vec<f64>>) {
// Objectives
let f1 = 4.0 * x[0].powi(2) + 4.0 * x[1].powi(2);
let f2 = (x[0] - 5.0).powi(2) + (x[1] - 5.0).powi(2);
// Constraints
let g1 = (x[0] - 5.0).powi(2) + x[1].powi(2) - 25.0;
let g2 = 7.7 - (x[0] - 8.0).powi(2) - (x[1] + 3.0).powi(2);
(vec![f1, f2], Some(vec![g1, g2]))
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Define the search space
let vars = vec![Variable(0.0, 5.0), Variable(0.0, 3.0)];
// 2. Configure the NSGA-II optimiser
let optimiser = Optimiser::Nsga {
pop_size: 50,
crossover: SbxParams::default(),
mutation: PmParams::default(),
seed: None,
};
// 3. Solve the problem
let result = optimiser.solve(&mut binh_and_korn, &vars, 250)?;
// 4. Get the Pareto front
println!("Found {} solutions on the Pareto front.", result.solutions.len());
Ok(())
}
After running the Binh and Korn example and plotting the solutions, you should see a Pareto front similar to the one shown below.
Similar to the Rust examples all blackbox functions must have signatures of the form:
def blackbox(x: list[float]) -> tuple[list[float], list[float] | None]
Here's the same Binh and Korn example in Python:
import swarm_py as sp
def binh_and_korn_problem(x):
f1 = 4.0 * x[0]**2 + 4.0 * x[1]**2
f2 = (x[0] - 5.0)**2 + (x[1] - 5.0)**2
g1 = (x[0] - 5.0)**2 + x[1]**2 - 25.0
g2 = 7.7 - (x[0] - 8.0)**2 - (x[1] + 3.0)**2
return ([f1, f2], [g1, g2])
if __name__ == "__main__":
# Define variable bounds and optimisation settings
variables = [sp.Variable(0, 5), sp.Variable(0, 3)]
# Configure the NSGA-II optimiser
# Other properties are default unless specified
optimiser = sp.Optimiser.nsga(pop_size=100)
# Run the optimisation
result = optimiser.solve(binh_and_korn_problem, variables, 250)
# For parallel execution, use the following:
result = optimiser.solve_par(binh_and_korn_problem, variables, 250)
print(result.solutions)
Comparing to the same configuration Pymoo NSGA-II optimiser for the ZDT1 problem:
For expensive blackbox functions it makes sense to run swarm in parallel. If we simulate an expensive blackbox function by adding a sleep delay to the ZDT1 problem, i.e.,
def expensive_blackbox(x):
time.sleep(0.0005) # Artificial delay
n_vars = len(x)
f1 = x[0]
g = 1.0 + 9.0 * sum(x[1:]) / (n_vars - 1)
h = 1.0 - np.sqrt(f1 / (g + 1e-8))
f2 = g * h
return ([f1, f2], None)
then running swarm with NSGA-II in parallel is a massive improvement:
These instructions assume that Python3 and Cargo are installed on your system. To set up this project, follow these steps:
git clone https://github.com/alexlovric/swarm.git
cd swarm/swarm_py
python3 -m venv .venv
source .venv/bin/activate # In windows /Scripts/activate
python3 -m pip install -r requirements.txt
maturin develop --release
maturin build --release
MIT License - See LICENSE for details.
If you'd like to support the project consider: