swarm-py


Nameswarm-py JSON
Version 0.1.2 PyPI version JSON
download
home_pageNone
SummaryFast blackbox optimisation tool written in rust, wrapped in python
upload_time2025-07-09 17:02:16
maintainerNone
docs_urlNone
authorAleksander Lovric <alexlovric@gmail.com>
requires_python>=3.10
licenseMIT
keywords optimisation blackbox particle-swarm pso nsga nsga2 nsga-ii scientific-computing
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Swarm
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:
- NSGA-II for single/multi-objective problems.
- Particle Swarm Optimisation (PSO) for single-objective problems.
- Optional Parallel Execution (massively improves performance for expensive blackbox functions).
- Flexible Function Support.

## Getting Started
### Rust
To use Swarm in your project, add it as a dependency in your Cargo.toml:

```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`.

### Python
```bash
pip install swarm_py
```

## Examples in Rust

All functions provided to Swarm must be of the signature: 
```rust
FnMut(&[f64]) -> (Vec<f64>, Option<Vec<f64>>)
```
- The first argument is a reference to a vector of f64 values representing the variables. Variable types are currently limited to f64.
- The first return vector contains the objective values.
- The second return vector contains the constraint values (if any).
- For parallel execution, the function must also be thread-safe (i.e., `Fn + Sync + Send`). 

### Single-Objective Optimisation with Particle Swarm (PSO)
Problem: Minimise f(x, y) = (x² + y - 11)² + (x + y² - 7)²

```rust
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(())
}
```

### Multi-Objective Optimisation with NSGA-II
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.

```rust
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.

<img src="https://github.com/alexlovric/swarm/blob/main/assets/binh_korn_pareto.png?raw=true" />

## Examples in Python

Similar to the Rust examples all blackbox functions must have signatures of the form:

```python
def blackbox(x: list[float]) -> tuple[list[float], list[float] | None]
```

Here's the same Binh and Korn example in Python:

```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)
```

## Performance (Python)

### Serial Execution
Comparing to the same configuration Pymoo NSGA-II optimiser for the ZDT1 problem:

<img src="https://github.com/alexlovric/swarm/blob/main/assets/zdt1_comp.png?raw=true" width="99%"/>

### Parallel Execution
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.,

```python
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:

<img src="https://github.com/alexlovric/swarm/blob/main/assets/parallel_serial_perf.png?raw=true" width="99%"/>

## Build python from source
These instructions assume that Python3 and Cargo are installed on your system. To set up this project, follow these steps:
1. Clone the repository:
    ```bash
    git clone https://github.com/alexlovric/swarm.git
    cd swarm/swarm_py
    ```
2. Create a virtual environment and install build system:
    ```bash
    python3 -m venv .venv
    source .venv/bin/activate # In windows /Scripts/activate
    python3 -m pip install -r requirements.txt
    ```
3. Build the release binary:
    ```bash
    maturin develop --release
    ```
4. Build the python wheel:
    ```bash
    maturin build --release
    ```

## License
MIT License - See [LICENSE](LICENSE) for details.

## Support
If you'd like to support the project consider:
- Identifying the features you'd like to see implemented or bugs you'd like to fix and open an issue.
- Contributing to the code by resolving existing issues, I'm happy to have you.
- Donating to help me continue development, [Buy Me a Coffee](https://coff.ee/alexlovric)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "swarm-py",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "optimisation, blackbox, particle-swarm, pso, nsga, nsga2, NSGA-II, scientific-computing",
    "author": "Aleksander Lovric <alexlovric@gmail.com>",
    "author_email": "Aleksander Lovric <alexlovric@gmail.com>",
    "download_url": null,
    "platform": null,
    "description": "# Swarm\nBlack-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:\n- NSGA-II for single/multi-objective problems.\n- Particle Swarm Optimisation (PSO) for single-objective problems.\n- Optional Parallel Execution (massively improves performance for expensive blackbox functions).\n- Flexible Function Support.\n\n## Getting Started\n### Rust\nTo use Swarm in your project, add it as a dependency in your Cargo.toml:\n\n```toml\n[dependencies]\nswarm = \"0.1.2\"  # (replace with current version)\n```\n\nThis 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`.\n\n### Python\n```bash\npip install swarm_py\n```\n\n## Examples in Rust\n\nAll functions provided to Swarm must be of the signature: \n```rust\nFnMut(&[f64]) -> (Vec<f64>, Option<Vec<f64>>)\n```\n- The first argument is a reference to a vector of f64 values representing the variables. Variable types are currently limited to f64.\n- The first return vector contains the objective values.\n- The second return vector contains the constraint values (if any).\n- For parallel execution, the function must also be thread-safe (i.e., `Fn + Sync + Send`). \n\n### Single-Objective Optimisation with Particle Swarm (PSO)\nProblem: Minimise f(x, y) = (x\u00b2 + y - 11)\u00b2 + (x + y\u00b2 - 7)\u00b2\n\n```rust\nuse swarm::{error::Result, pso::PsoParams, Optimiser, Variable};\n\n// Define the function (can also use closure)\nfn himmelblau(x: &[f64]) -> (Vec<f64>, Option<Vec<f64>>) {\n    let f = (x[0].powi(2) + x[1] - 11.0).powi(2) + (x[0] + x[1].powi(2) - 7.0).powi(2);\n    (vec![f], None)\n}\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    // 1. Define the search space for the variables\n    let vars = vec![Variable(-5.0, 5.0), Variable(-5.0, 5.0)];\n\n    // 2. Configure the optimiser\n    let optimiser = Optimiser::Pso {\n        n_particles: 100,\n        params: PsoParams::default(),\n        constraint_handler: None,\n        seed: None,\n    };\n\n    // 3. Find the minimum\n    let max_iter = 200;\n    let result = optimiser.solve(&mut himmelblau, &vars, max_iter)?;\n    // Note: 1. You can also call the pso function directly.\n    // Note: 2. You can pass a closure directly.\n    // Note: 3. For parallel execution use `solve_par` \n    // (if feature `parallel` is enabled)\n    \n    // 4. Get the best solution found\n    let best = &result.solutions[0];\n    println!(\"Min at x = {:.4?}, f(x) = {:.4}\", best.x, best.f[0]);\n    Ok(())\n}\n```\n\n### Multi-Objective Optimisation with NSGA-II\nThis 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.\n\nProblem: Minimise two objectives, f1(x, y) and f2(x, y), subject to two constraints.\n\n```rust\nuse swarm::{Optimiser, Variable, SbxParams, PmParams};\n\n// Define the problem (can also use closure)\nfn binh_and_korn(x: &[f64]) -> (Vec<f64>, Option<Vec<f64>>) {\n    // Objectives\n    let f1 = 4.0 * x[0].powi(2) + 4.0 * x[1].powi(2);\n    let f2 = (x[0] - 5.0).powi(2) + (x[1] - 5.0).powi(2);\n\n    // Constraints\n    let g1 = (x[0] - 5.0).powi(2) + x[1].powi(2) - 25.0;\n    let g2 = 7.7 - (x[0] - 8.0).powi(2) - (x[1] + 3.0).powi(2);\n\n    (vec![f1, f2], Some(vec![g1, g2]))\n}\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    // 1. Define the search space\n    let vars = vec![Variable(0.0, 5.0), Variable(0.0, 3.0)];\n\n    // 2. Configure the NSGA-II optimiser\n    let optimiser = Optimiser::Nsga {\n        pop_size: 50,\n        crossover: SbxParams::default(),\n        mutation: PmParams::default(),\n        seed: None,\n    };\n\n    // 3. Solve the problem\n    let result = optimiser.solve(&mut binh_and_korn, &vars, 250)?;\n    \n    // 4. Get the Pareto front\n    println!(\"Found {} solutions on the Pareto front.\", result.solutions.len());\n    \n    Ok(())\n}\n```\n\nAfter running the Binh and Korn example and plotting the solutions, you should see a Pareto front similar to the one shown below.\n\n<img src=\"https://github.com/alexlovric/swarm/blob/main/assets/binh_korn_pareto.png?raw=true\" />\n\n## Examples in Python\n\nSimilar to the Rust examples all blackbox functions must have signatures of the form:\n\n```python\ndef blackbox(x: list[float]) -> tuple[list[float], list[float] | None]\n```\n\nHere's the same Binh and Korn example in Python:\n\n```python\nimport swarm_py as sp\n\ndef binh_and_korn_problem(x):\n    f1 = 4.0 * x[0]**2 + 4.0 * x[1]**2\n    f2 = (x[0] - 5.0)**2 + (x[1] - 5.0)**2\n\n    g1 = (x[0] - 5.0)**2 + x[1]**2 - 25.0\n    g2 = 7.7 - (x[0] - 8.0)**2 - (x[1] + 3.0)**2\n    return ([f1, f2], [g1, g2])\n\nif __name__ == \"__main__\":\n    # Define variable bounds and optimisation settings\n    variables = [sp.Variable(0, 5), sp.Variable(0, 3)]\n\n    # Configure the NSGA-II optimiser\n    # Other properties are default unless specified\n    optimiser = sp.Optimiser.nsga(pop_size=100)\n\n    # Run the optimisation\n    result = optimiser.solve(binh_and_korn_problem, variables, 250)\n\n    # For parallel execution, use the following:\n    result = optimiser.solve_par(binh_and_korn_problem, variables, 250)\n\n    print(result.solutions)\n```\n\n## Performance (Python)\n\n### Serial Execution\nComparing to the same configuration Pymoo NSGA-II optimiser for the ZDT1 problem:\n\n<img src=\"https://github.com/alexlovric/swarm/blob/main/assets/zdt1_comp.png?raw=true\" width=\"99%\"/>\n\n### Parallel Execution\nFor 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.,\n\n```python\ndef expensive_blackbox(x):\n    time.sleep(0.0005)  # Artificial delay\n    n_vars = len(x)\n    f1 = x[0]\n    g = 1.0 + 9.0 * sum(x[1:]) / (n_vars - 1)\n    h = 1.0 - np.sqrt(f1 / (g + 1e-8))\n    f2 = g * h\n    return ([f1, f2], None)\n```\n\nthen running swarm with NSGA-II in parallel is a massive improvement:\n\n<img src=\"https://github.com/alexlovric/swarm/blob/main/assets/parallel_serial_perf.png?raw=true\" width=\"99%\"/>\n\n## Build python from source\nThese instructions assume that Python3 and Cargo are installed on your system. To set up this project, follow these steps:\n1. Clone the repository:\n    ```bash\n    git clone https://github.com/alexlovric/swarm.git\n    cd swarm/swarm_py\n    ```\n2. Create a virtual environment and install build system:\n    ```bash\n    python3 -m venv .venv\n    source .venv/bin/activate # In windows /Scripts/activate\n    python3 -m pip install -r requirements.txt\n    ```\n3. Build the release binary:\n    ```bash\n    maturin develop --release\n    ```\n4. Build the python wheel:\n    ```bash\n    maturin build --release\n    ```\n\n## License\nMIT License - See [LICENSE](LICENSE) for details.\n\n## Support\nIf you'd like to support the project consider:\n- Identifying the features you'd like to see implemented or bugs you'd like to fix and open an issue.\n- Contributing to the code by resolving existing issues, I'm happy to have you.\n- Donating to help me continue development, [Buy Me a Coffee](https://coff.ee/alexlovric)\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Fast blackbox optimisation tool written in rust, wrapped in python",
    "version": "0.1.2",
    "project_urls": {
        "Source Code": "https://github.com/alexlovric/swarm"
    },
    "split_keywords": [
        "optimisation",
        " blackbox",
        " particle-swarm",
        " pso",
        " nsga",
        " nsga2",
        " nsga-ii",
        " scientific-computing"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "0c40ef37c2dfba02cc04b2d5f8050b0b770cd1792299dae55e8b60c995247f99",
                "md5": "de0ad98964fe868a99dc560b2d367e66",
                "sha256": "fb260a1ed4a14bc1e1303d57d4c12ecd51e0f08afd0f9ecbb7a8a8137791c7b9"
            },
            "downloads": -1,
            "filename": "swarm_py-0.1.2-cp38-abi3-macosx_10_12_x86_64.whl",
            "has_sig": false,
            "md5_digest": "de0ad98964fe868a99dc560b2d367e66",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.10",
            "size": 383640,
            "upload_time": "2025-07-09T17:02:16",
            "upload_time_iso_8601": "2025-07-09T17:02:16.956158Z",
            "url": "https://files.pythonhosted.org/packages/0c/40/ef37c2dfba02cc04b2d5f8050b0b770cd1792299dae55e8b60c995247f99/swarm_py-0.1.2-cp38-abi3-macosx_10_12_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ce72091aeabc7b16c61f0fe6be18c3f7578b1e99725f4632a831e9e73512eef2",
                "md5": "f68f274c6af9d117690251df998151a7",
                "sha256": "8fe83e0fda2d7a2eb503bfb7c32d0c999b20c38b7c973a109e7cf582ee112657"
            },
            "downloads": -1,
            "filename": "swarm_py-0.1.2-cp38-abi3-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "f68f274c6af9d117690251df998151a7",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.10",
            "size": 376278,
            "upload_time": "2025-07-09T17:02:18",
            "upload_time_iso_8601": "2025-07-09T17:02:18.706373Z",
            "url": "https://files.pythonhosted.org/packages/ce/72/091aeabc7b16c61f0fe6be18c3f7578b1e99725f4632a831e9e73512eef2/swarm_py-0.1.2-cp38-abi3-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "7ced60b6d48daf44fcefbbdb76dfa335067af7b8229da8231eab194b114d8471",
                "md5": "1d7b5255aba77008c86e0a3a04055c8a",
                "sha256": "6b347c4f5f814e35031cda50e2438cbfbe42a0727d3b8f034486ab9f52c90523"
            },
            "downloads": -1,
            "filename": "swarm_py-0.1.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "1d7b5255aba77008c86e0a3a04055c8a",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.10",
            "size": 425619,
            "upload_time": "2025-07-09T17:02:20",
            "upload_time_iso_8601": "2025-07-09T17:02:20.077399Z",
            "url": "https://files.pythonhosted.org/packages/7c/ed/60b6d48daf44fcefbbdb76dfa335067af7b8229da8231eab194b114d8471/swarm_py-0.1.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "321b3cddfb56654855da9e58c2763da65560e20ca864b9825fdd0db9aa9e1b68",
                "md5": "eac53c7f4330b149408cb3bb0d30d202",
                "sha256": "ed2d3ceacacc846c68f7a9d5f867e19a7129d524ad11c985d2099d86836155d4"
            },
            "downloads": -1,
            "filename": "swarm_py-0.1.2-cp38-abi3-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "eac53c7f4330b149408cb3bb0d30d202",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.10",
            "size": 258258,
            "upload_time": "2025-07-09T17:02:21",
            "upload_time_iso_8601": "2025-07-09T17:02:21.649651Z",
            "url": "https://files.pythonhosted.org/packages/32/1b/3cddfb56654855da9e58c2763da65560e20ca864b9825fdd0db9aa9e1b68/swarm_py-0.1.2-cp38-abi3-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-09 17:02:16",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "alexlovric",
    "github_project": "swarm",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "swarm-py"
}
        
Elapsed time: 0.41572s