tiny-solver


Nametiny-solver JSON
Version 0.7.0 PyPI version JSON
download
home_pagehttps://github.com/powei-lin/tiny-solver-rs
SummaryFactor graph solver
upload_time2024-10-14 21:40:05
maintainerNone
docs_urlNone
authorPowei Lin <poweilin1994@gmail.com>
requires_python>=3.8
licenseMIT OR Apache-2.0
keywords factor-graph ceres-solver minisam
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # tiny-solver-rs
> Warning! This project is still under development.

[![crate](https://img.shields.io/crates/v/tiny-solver.svg)](https://crates.io/crates/tiny-solver)
[![PyPI - Version](https://img.shields.io/pypi/v/tiny-solver.svg)](https://pypi.org/project/tiny-solver)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tiny-solver.svg)](https://pypi.org/project/tiny-solver)

Inspired by [ceres-solver](https://github.com/ceres-solver/ceres-solver), [tiny-solver](https://github.com/keir/tinysolver), and [minisam](https://github.com/dongjing3309/minisam).

This is a general optimizer written in Rust, including bindings for Python. If you're familiar with ceres-solver or factor-graph optimizers, you'll find it very easy to use.

## Installation
### python
The python package can be installed directly from PyPI:
```sh
pip install tiny-solver
```
### rust
```sh
cargo add tiny-solver
```

## Current Features

- [x] Automatic Derivatives using [num-dual](https://github.com/itt-ustutt/num-dual)
- [x] ~~Sparse QR~~, Sparse Cholesky using [faer](https://github.com/sarah-ek/faer-rs)
- [x] GaussNewtonOptimizer
- [x] Multithreading jacobian
- [x] loss function (Huber)
- [x] Define factor in python

#### TODO
- [ ] LevenbergMarquardtOptimizer
- [ ] information matrix

## Benchmark
| dataset | tiny-solver | gtsam   | minisam  |
|---------|-------------|---------|----------|
| m3500   | 161.1ms     | 130.7ms | 123.6 ms |
|         |             |         |          |

It's not extremely optimized, but it's easy to install and use.

## Usage
Rust 
```rust
// define your own Cost/Factor struct
// impl residual function
// and the jacobian will be auto generated
struct CustomFactor {}
impl tiny_solver::factors::Factor for CustomFactor {
    fn residual_func(
        &self,
        params: &[nalgebra::DVector<num_dual::DualDVec64>],
    ) -> nalgebra::DVector<num_dual::DualDVec64> {
        let x = &params[0][0];
        let y = &params[1][0];
        let z = &params[1][1];

        na::dvector![x + y.clone().mul(2.0) + z.clone().mul(4.0), y.mul(z)]
    }
}

fn main() {
    // init logger, `export RUST_LOG=trace` to see more log
    env_logger::init();

    // init problem (factor graph)
    let mut problem = tiny_solver::Problem::new();

    // add residual blocks (factors)
    // add residual x needs to be close to 3.0
    problem.add_residual_block(
        1,
        vec![("x".to_string(), 1)],
        Box::new(tiny_solver::factors::PriorFactor {
            v: na::dvector![3.0],
        }),
        None,
    );
    // add custom residual for x and yz
    problem.add_residual_block(
        2,
        vec![("x".to_string(), 1), ("yz".to_string(), 2)],
        Box::new(CustomFactor {}),
        None,
    );

    // the initial values for x is 0.7 and yz is [-30.2, 123.4]
    let initial_values = HashMap::<String, na::DVector<f64>>::from([
        ("x".to_string(), na::dvector![0.7]),
        ("yz".to_string(), na::dvector![-30.2, 123.4]),
    ]);

    // initialize optimizer
    let optimizer = tiny_solver::GaussNewtonOptimizer {};

    // optimize
    let result = optimizer.optimize(&problem, &initial_values, None);

    // result
    for (k, v) in result {
        println!("{}: {}", k, v);
    }
}
```
Python
```py
import numpy as np
from tiny_solver import Problem, GaussNewtonOptimizer
from tiny_solver.factors import PriorFactor, PyFactor

# define custom cost function in python
# the trade off is the jacobian for the problem cannot be done in parallel
# because of gil
def cost(x: np.ndarray, yz: np.ndarray) -> np.ndarray:
    r0 = x[0] + 2 * yz[0] + 4 * yz[1]
    r1 = yz[0] * yz[0]
    return np.array([r0, r1])


def main():

    # initialize problem (factor graph)
    problem = Problem()

    # factor defined in python
    custom_factor = PyFactor(cost)
    problem.add_residual_block(
        2,
        [
            ("x", 1),
            ("yz", 2),
        ],
        custom_factor,
        None,
    )

    # prior factor import from rust
    prior_factor = PriorFactor(np.array([3.0]))
    problem.add_residual_block(1, [("x", 1)], prior_factor, None)

    # initial values
    init_values = {"x": np.array([0.7]), "yz": np.array([-30.2, 123.4])}

    # optimizer
    optimizer = GaussNewtonOptimizer()
    result_values = optimizer.optimize(problem, init_values)

    # result
    for k, v in result_values.items():
        print(f"{k}: {v}")


if __name__ == "__main__":
    main()
```

## Example
### Basic example
```sh
cargo run -r --example small_problem
```
### M3500 dataset
<img src="docs/m3500_rs.png" width="300" alt="m3500 dataset rust result.">

```sh
git clone https://github.com/powei-lin/tiny-solver-rs.git
cd tiny-solver-rs

# run rust version
cargo run -r --example m3500_benchmar

# run python version
pip install tiny-solver matplotlib
python3 examples/python/m3500.py
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/powei-lin/tiny-solver-rs",
    "name": "tiny-solver",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "factor-graph, ceres-solver, minisam",
    "author": "Powei Lin <poweilin1994@gmail.com>",
    "author_email": "Powei Lin <poweilin1994@gmail.com>",
    "download_url": null,
    "platform": null,
    "description": "# tiny-solver-rs\n> Warning! This project is still under development.\n\n[![crate](https://img.shields.io/crates/v/tiny-solver.svg)](https://crates.io/crates/tiny-solver)\n[![PyPI - Version](https://img.shields.io/pypi/v/tiny-solver.svg)](https://pypi.org/project/tiny-solver)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tiny-solver.svg)](https://pypi.org/project/tiny-solver)\n\nInspired by [ceres-solver](https://github.com/ceres-solver/ceres-solver), [tiny-solver](https://github.com/keir/tinysolver), and [minisam](https://github.com/dongjing3309/minisam).\n\nThis is a general optimizer written in Rust, including bindings for Python. If you're familiar with ceres-solver or factor-graph optimizers, you'll find it very easy to use.\n\n## Installation\n### python\nThe python package can be installed directly from PyPI:\n```sh\npip install tiny-solver\n```\n### rust\n```sh\ncargo add tiny-solver\n```\n\n## Current Features\n\n- [x] Automatic Derivatives using [num-dual](https://github.com/itt-ustutt/num-dual)\n- [x] ~~Sparse QR~~, Sparse Cholesky using [faer](https://github.com/sarah-ek/faer-rs)\n- [x] GaussNewtonOptimizer\n- [x] Multithreading jacobian\n- [x] loss function (Huber)\n- [x] Define factor in python\n\n#### TODO\n- [ ] LevenbergMarquardtOptimizer\n- [ ] information matrix\n\n## Benchmark\n| dataset | tiny-solver | gtsam   | minisam  |\n|---------|-------------|---------|----------|\n| m3500   | 161.1ms     | 130.7ms | 123.6 ms |\n|         |             |         |          |\n\nIt's not extremely optimized, but it's easy to install and use.\n\n## Usage\nRust \n```rust\n// define your own Cost/Factor struct\n// impl residual function\n// and the jacobian will be auto generated\nstruct CustomFactor {}\nimpl tiny_solver::factors::Factor for CustomFactor {\n    fn residual_func(\n        &self,\n        params: &[nalgebra::DVector<num_dual::DualDVec64>],\n    ) -> nalgebra::DVector<num_dual::DualDVec64> {\n        let x = &params[0][0];\n        let y = &params[1][0];\n        let z = &params[1][1];\n\n        na::dvector![x + y.clone().mul(2.0) + z.clone().mul(4.0), y.mul(z)]\n    }\n}\n\nfn main() {\n    // init logger, `export RUST_LOG=trace` to see more log\n    env_logger::init();\n\n    // init problem (factor graph)\n    let mut problem = tiny_solver::Problem::new();\n\n    // add residual blocks (factors)\n    // add residual x needs to be close to 3.0\n    problem.add_residual_block(\n        1,\n        vec![(\"x\".to_string(), 1)],\n        Box::new(tiny_solver::factors::PriorFactor {\n            v: na::dvector![3.0],\n        }),\n        None,\n    );\n    // add custom residual for x and yz\n    problem.add_residual_block(\n        2,\n        vec![(\"x\".to_string(), 1), (\"yz\".to_string(), 2)],\n        Box::new(CustomFactor {}),\n        None,\n    );\n\n    // the initial values for x is 0.7 and yz is [-30.2, 123.4]\n    let initial_values = HashMap::<String, na::DVector<f64>>::from([\n        (\"x\".to_string(), na::dvector![0.7]),\n        (\"yz\".to_string(), na::dvector![-30.2, 123.4]),\n    ]);\n\n    // initialize optimizer\n    let optimizer = tiny_solver::GaussNewtonOptimizer {};\n\n    // optimize\n    let result = optimizer.optimize(&problem, &initial_values, None);\n\n    // result\n    for (k, v) in result {\n        println!(\"{}: {}\", k, v);\n    }\n}\n```\nPython\n```py\nimport numpy as np\nfrom tiny_solver import Problem, GaussNewtonOptimizer\nfrom tiny_solver.factors import PriorFactor, PyFactor\n\n# define custom cost function in python\n# the trade off is the jacobian for the problem cannot be done in parallel\n# because of gil\ndef cost(x: np.ndarray, yz: np.ndarray) -> np.ndarray:\n    r0 = x[0] + 2 * yz[0] + 4 * yz[1]\n    r1 = yz[0] * yz[0]\n    return np.array([r0, r1])\n\n\ndef main():\n\n    # initialize problem (factor graph)\n    problem = Problem()\n\n    # factor defined in python\n    custom_factor = PyFactor(cost)\n    problem.add_residual_block(\n        2,\n        [\n            (\"x\", 1),\n            (\"yz\", 2),\n        ],\n        custom_factor,\n        None,\n    )\n\n    # prior factor import from rust\n    prior_factor = PriorFactor(np.array([3.0]))\n    problem.add_residual_block(1, [(\"x\", 1)], prior_factor, None)\n\n    # initial values\n    init_values = {\"x\": np.array([0.7]), \"yz\": np.array([-30.2, 123.4])}\n\n    # optimizer\n    optimizer = GaussNewtonOptimizer()\n    result_values = optimizer.optimize(problem, init_values)\n\n    # result\n    for k, v in result_values.items():\n        print(f\"{k}: {v}\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n## Example\n### Basic example\n```sh\ncargo run -r --example small_problem\n```\n### M3500 dataset\n<img src=\"docs/m3500_rs.png\" width=\"300\" alt=\"m3500 dataset rust result.\">\n\n```sh\ngit clone https://github.com/powei-lin/tiny-solver-rs.git\ncd tiny-solver-rs\n\n# run rust version\ncargo run -r --example m3500_benchmar\n\n# run python version\npip install tiny-solver matplotlib\npython3 examples/python/m3500.py\n```\n",
    "bugtrack_url": null,
    "license": "MIT OR Apache-2.0",
    "summary": "Factor graph solver",
    "version": "0.7.0",
    "project_urls": {
        "Homepage": "https://github.com/powei-lin/tiny-solver-rs",
        "Source Code": "https://github.com/powei-lin/tiny-solver-rs"
    },
    "split_keywords": [
        "factor-graph",
        " ceres-solver",
        " minisam"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "431e387913676301a7efeb46af37ae62ef7139670e1095a3dcba037233613f58",
                "md5": "970de8609b90da0fee68ceb20ef7a63e",
                "sha256": "9898dae2bdf83eec9958c2caacef1c49d0f1fd3582cbcb27185170f9e194b91b"
            },
            "downloads": -1,
            "filename": "tiny_solver-0.7.0-cp37-abi3-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "970de8609b90da0fee68ceb20ef7a63e",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.8",
            "size": 2884042,
            "upload_time": "2024-10-14T21:40:05",
            "upload_time_iso_8601": "2024-10-14T21:40:05.247097Z",
            "url": "https://files.pythonhosted.org/packages/43/1e/387913676301a7efeb46af37ae62ef7139670e1095a3dcba037233613f58/tiny_solver-0.7.0-cp37-abi3-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4c128f50cf53e86c46b02d20ae0e9dddcf376e5d3babb3362e2427656bff477a",
                "md5": "0e0b684ea96a32b3839ae99a47e30c81",
                "sha256": "a1c491a74978307b2f74da3685a610a5aa6ac63c1a219d22ba3c6f716cf4a973"
            },
            "downloads": -1,
            "filename": "tiny_solver-0.7.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "has_sig": false,
            "md5_digest": "0e0b684ea96a32b3839ae99a47e30c81",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.8",
            "size": 4207472,
            "upload_time": "2024-10-14T21:44:07",
            "upload_time_iso_8601": "2024-10-14T21:44:07.693564Z",
            "url": "https://files.pythonhosted.org/packages/4c/12/8f50cf53e86c46b02d20ae0e9dddcf376e5d3babb3362e2427656bff477a/tiny_solver-0.7.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a94ba1542c28dbaf84bb68dfb8e5272c806499f5a7c396be3b72a7a07161c8b6",
                "md5": "2ca0e87f16794a3ee2fc3f503611d451",
                "sha256": "bb3b4886e27eaa0dce79ab0f5c64577732f516b369737005aaa190918818ff2c"
            },
            "downloads": -1,
            "filename": "tiny_solver-0.7.0-cp37-abi3-manylinux_2_35_x86_64.whl",
            "has_sig": false,
            "md5_digest": "2ca0e87f16794a3ee2fc3f503611d451",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.8",
            "size": 3617814,
            "upload_time": "2024-10-14T21:44:10",
            "upload_time_iso_8601": "2024-10-14T21:44:10.926972Z",
            "url": "https://files.pythonhosted.org/packages/a9/4b/a1542c28dbaf84bb68dfb8e5272c806499f5a7c396be3b72a7a07161c8b6/tiny_solver-0.7.0-cp37-abi3-manylinux_2_35_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-10-14 21:40:05",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "powei-lin",
    "github_project": "tiny-solver-rs",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "tiny-solver"
}
        
Elapsed time: 1.43936s