Skip to main content

Factor graph solver

Project description

tiny-solver-rs

Warning! This project is still under development.

crate PyPI - Version PyPI - Python Version

Inspired by ceres-solver, tiny-solver, and 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:

pip install tiny-solver

rust

cargo add tiny-solver

Current Features

  • Automatic Derivatives using num-dual
  • Sparse QR, Sparse Cholesky using faer
  • GaussNewtonOptimizer
  • Multithreading jacobian
  • loss function (Huber)
  • 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

// 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

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

cargo run -r --example small_problem

M3500 dataset

m3500 dataset rust result.
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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

tiny_solver-0.7.0-cp37-abi3-manylinux_2_35_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.7+ manylinux: glibc 2.35+ x86-64

tiny_solver-0.7.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (4.2 MB view details)

Uploaded CPython 3.7+ manylinux: glibc 2.17+ ARM64

tiny_solver-0.7.0-cp37-abi3-macosx_11_0_arm64.whl (2.9 MB view details)

Uploaded CPython 3.7+ macOS 11.0+ ARM64

File details

Details for the file tiny_solver-0.7.0-cp37-abi3-manylinux_2_35_x86_64.whl.

File metadata

File hashes

Hashes for tiny_solver-0.7.0-cp37-abi3-manylinux_2_35_x86_64.whl
Algorithm Hash digest
SHA256 bb3b4886e27eaa0dce79ab0f5c64577732f516b369737005aaa190918818ff2c
MD5 2ca0e87f16794a3ee2fc3f503611d451
BLAKE2b-256 a94ba1542c28dbaf84bb68dfb8e5272c806499f5a7c396be3b72a7a07161c8b6

See more details on using hashes here.

File details

Details for the file tiny_solver-0.7.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for tiny_solver-0.7.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 a1c491a74978307b2f74da3685a610a5aa6ac63c1a219d22ba3c6f716cf4a973
MD5 0e0b684ea96a32b3839ae99a47e30c81
BLAKE2b-256 4c128f50cf53e86c46b02d20ae0e9dddcf376e5d3babb3362e2427656bff477a

See more details on using hashes here.

File details

Details for the file tiny_solver-0.7.0-cp37-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for tiny_solver-0.7.0-cp37-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9898dae2bdf83eec9958c2caacef1c49d0f1fd3582cbcb27185170f9e194b91b
MD5 970de8609b90da0fee68ceb20ef7a63e
BLAKE2b-256 431e387913676301a7efeb46af37ae62ef7139670e1095a3dcba037233613f58

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page