Factor graphs in rust
84
stars
266
commits
Rust
primary language
May 22, 2026
updated
fact.rs (pronounced factors) is a nonlinear least squares optimization library over factor graphs written in Rust.
It is specifically geared toward sensor fusion in robotics. It aims to be fast, easy to use, and safe. The fact.rs API takes heavy inspiration from the gtsam library.
Currently, it supports the following features
We recommend you checkout the docs for more info. For usage, simply add factrs to your Cargo.toml and start using it!
There's a number of examples found in the examples folder, including loading g20 files, serialization, and custom factors.
To run a simple pose graph optimization, simply clone this repository and run,
cargo run --release --example g2o ./examples/data/M3500.g2o
to visualize the optimization steps with rerun simply add --features rerun to the above command.
Running the other examples can be done similarly,
cargo run --release --example gps
cargo run --release --example serde --features serde
Additionally, we recommend checking out the tests folder for more examples of how to make custom noise models, residuals, robust kernels, and variables.
use factrs::{
assign_symbols,
core::{BetweenResidual, GaussNewton, Graph, Huber, PriorResidual, Values, SO2},
fac,
traits::*,
};
// Assign symbols to variable types
assign_symbols!(X: SO2);
fn main() {
// Make all the values
let mut values = Values::new();
let x = SO2::from_theta(1.0);
let y = SO2::from_theta(2.0);
values.insert(X(0), SO2::identity());
values.insert(X(1), SO2::identity());
// Make the factors & insert into graph
let mut graph = Graph::new();
let res = PriorResidual::new(x.clone());
let factor = fac![res, X(0)];
graph.add_factor(factor);
let res = BetweenResidual::new(y.minus(&x));
let factor = fac![res, (X(0), X(1)), 0.1 as std, Huber::default()];
graph.add_factor(factor);
// Optimize!
let mut opt: GaussNewton = GaussNewton::new_default(graph);
let result = opt.optimize(values).unwrap();
println!("Results {:#}", result);
}
fact.rs leans into the Rust way of doing things, and attempts to compile-time error as much as possible. This includes the following,
A few examples,
use factrs::core::{assign_symbols, fac, PriorResidual, Values, VectorVar2, SO2};
// Assign symbols to variable types
assign_symbols(X: SO2, Y: SO2);
let mut values = Values::new();
// Proper usage
let id = SO2::identity();
values.insert(X(0), id);
let prior = PriorResidual::new(id);
let f = fac![prior, X(0), (0.1, 0.2) as std];
// These will all compile-time error
// mismatched symbol-variable types
values.insert(X(5), VectorVar2::identity());
// wrong number of keys
let f = fac![PriorResidual::new(id), (X(0), X(1))];
// wrong noise-model dimension
let n = GaussianNoise::<5>::from_scalar_sigma(0.1);
let f = fac![PriorResidual::new(id), X(0), n];
// mismatched symbol-variable types
let f = fac![PriorResidual::new(id), Y(0), 0.1 as std];
Performance-wise, factrs is the fastest Rust library, and competitive with other C++ libraries. Benchmarks were ran on a 12th Gen Intel i9 and are all single-threaded (for now). Current benchmarks include sophus-rs, tiny-solver-rs, gtsam, and ceres. Data can be found in the examples/data folder.
Note, gtsam and Ceres are faster for the parking garage due to leveraging the sparsity of the pose graph better using the Baye's tree, something that is planned for factrs.
To run the rust benchmarks after cloning, simply run,
cargo bench -p factrs-bench
and the C++ benchmarks can be run with,
cmake -B build factrs-bench/cpp
cmake --build build
./build/bench
both of which have alias commands in the root justfile (which also includes a plotting alias).
Simply add via cargo as you do any rust dependency,
cargo add factrs
Contributions are more than welcome! Feel free to open an issue or a pull request with any ideas, bugs, features, etc you might have or want.
We feel rust and robotics are a good match and want to see rust robotics libraries catch-up to their C++ counterparts.
Rust
91.9%
C++
4.9%
Python
1.1%
Factor graphs in rust
84
stars
266
commits
Rust
primary language
May 22, 2026
updated
fact.rs (pronounced factors) is a nonlinear least squares optimization library over factor graphs written in Rust.
It is specifically geared toward sensor fusion in robotics. It aims to be fast, easy to use, and safe. The fact.rs API takes heavy inspiration from the gtsam library.
Currently, it supports the following features
We recommend you checkout the docs for more info. For usage, simply add factrs to your Cargo.toml and start using it!
There's a number of examples found in the examples folder, including loading g20 files, serialization, and custom factors.
To run a simple pose graph optimization, simply clone this repository and run,
cargo run --release --example g2o ./examples/data/M3500.g2o
to visualize the optimization steps with rerun simply add --features rerun to the above command.
Running the other examples can be done similarly,
cargo run --release --example gps
cargo run --release --example serde --features serde
Additionally, we recommend checking out the tests folder for more examples of how to make custom noise models, residuals, robust kernels, and variables.
use factrs::{
assign_symbols,
core::{BetweenResidual, GaussNewton, Graph, Huber, PriorResidual, Values, SO2},
fac,
traits::*,
};
// Assign symbols to variable types
assign_symbols!(X: SO2);
fn main() {
// Make all the values
let mut values = Values::new();
let x = SO2::from_theta(1.0);
let y = SO2::from_theta(2.0);
values.insert(X(0), SO2::identity());
values.insert(X(1), SO2::identity());
// Make the factors & insert into graph
let mut graph = Graph::new();
let res = PriorResidual::new(x.clone());
let factor = fac![res, X(0)];
graph.add_factor(factor);
let res = BetweenResidual::new(y.minus(&x));
let factor = fac![res, (X(0), X(1)), 0.1 as std, Huber::default()];
graph.add_factor(factor);
// Optimize!
let mut opt: GaussNewton = GaussNewton::new_default(graph);
let result = opt.optimize(values).unwrap();
println!("Results {:#}", result);
}
fact.rs leans into the Rust way of doing things, and attempts to compile-time error as much as possible. This includes the following,
A few examples,
use factrs::core::{assign_symbols, fac, PriorResidual, Values, VectorVar2, SO2};
// Assign symbols to variable types
assign_symbols(X: SO2, Y: SO2);
let mut values = Values::new();
// Proper usage
let id = SO2::identity();
values.insert(X(0), id);
let prior = PriorResidual::new(id);
let f = fac![prior, X(0), (0.1, 0.2) as std];
// These will all compile-time error
// mismatched symbol-variable types
values.insert(X(5), VectorVar2::identity());
// wrong number of keys
let f = fac![PriorResidual::new(id), (X(0), X(1))];
// wrong noise-model dimension
let n = GaussianNoise::<5>::from_scalar_sigma(0.1);
let f = fac![PriorResidual::new(id), X(0), n];
// mismatched symbol-variable types
let f = fac![PriorResidual::new(id), Y(0), 0.1 as std];
Performance-wise, factrs is the fastest Rust library, and competitive with other C++ libraries. Benchmarks were ran on a 12th Gen Intel i9 and are all single-threaded (for now). Current benchmarks include sophus-rs, tiny-solver-rs, gtsam, and ceres. Data can be found in the examples/data folder.
Note, gtsam and Ceres are faster for the parking garage due to leveraging the sparsity of the pose graph better using the Baye's tree, something that is planned for factrs.
To run the rust benchmarks after cloning, simply run,
cargo bench -p factrs-bench
and the C++ benchmarks can be run with,
cmake -B build factrs-bench/cpp
cmake --build build
./build/bench
both of which have alias commands in the root justfile (which also includes a plotting alias).
Simply add via cargo as you do any rust dependency,
cargo add factrs
Contributions are more than welcome! Feel free to open an issue or a pull request with any ideas, bugs, features, etc you might have or want.
We feel rust and robotics are a good match and want to see rust robotics libraries catch-up to their C++ counterparts.
Rust
91.9%
C++
4.9%
Python
1.1%