Getting Started
cartan is a Riemannian geometry library for Rust. It provides a backend-agnostic
trait system over const-generic manifolds, correct numerics near singularities, and an
optional discrete exterior calculus layer for covariant PDE solvers.
Installation
Rust
Add cartan to your Cargo.toml:
[dependencies]
cartan = "0.1"Or use cargo add:
cargo add cartanRequires Rust 1.85+ (2024 edition, const generics, RPITIT).
Python
cartan has full Python bindings via PyPI. A single abi3 wheel covers Python 3.9+:
pip install cartanThe Python API mirrors the Rust API with zero-copy numpy interop:
import cartan
import numpy as np
s2 = cartan.Sphere(2)
p = s2.random_point(seed=42)
q = s2.random_point(seed=43)
d = s2.dist(p, q)
print(f"dist(p, q) = {d:.6f}")All manifolds, optimizers, geodesic tools, and DEC operators are available from Python.
The Prelude
use cartan::prelude::*;The prelude re-exports the core traits and every manifold so you can import them in one line:
| Symbol | What it brings in |
|---|---|
Manifold | The root trait: tangent vectors, exp, log, distance |
Retraction | First-order retraction replacing exp for optimization |
Connection | Affine connection and covariant derivative |
ParallelTransport | Exact parallel transport along geodesics |
VectorTransport | Approximate transport (cheaper than ParallelTransport) |
Curvature | Sectional, Ricci, and scalar curvature tensors |
GeodesicInterpolation | Riemannian midpoint and geodesic interpolation |
| All manifolds | Euclidean, Sphere, SO, SPD, Grassmann, … |
First Example: Geodesic Roundtrip on the 2-Sphere
use cartan::prelude::*;
use cartan::manifolds::Sphere;
fn main() {
let s2 = Sphere::<3>; // 2-sphere S² embedded in R³
let mut rng = rand::rng();
// Sample a random base point and tangent vector.
let p = s2.random_point(&mut rng);
let v = s2.random_tangent(&p, &mut rng);
// Walk along the geodesic: q = exp_p(v).
let q = s2.exp(&p, &v);
// Recover the tangent: log_p(q) should equal v to machine precision.
let v_recovered = s2.log(&p, &q).unwrap();
assert!((v; v_recovered).norm() < 1e-10);
println!("Roundtrip error: {:.2e}", (v; v_recovered).norm());
}The const generic Sphere::<3> encodes the ambient dimension; the manifold
dimension is N; 1 = 2. No heap allocation, no runtime type dispatch.
Handling antipodal points
log returns None when p and q are antipodal (the log map is
undefined at the cut locus). Always match the result or use unwrap_or:
match s2.log(&p, &antipodal) {
Some(v) => { /* proceed */ }
None => { /* p and q are antipodal; choose alternate path */ }
}Trait Hierarchy
The six core traits layer capabilities from basic geometry to full curvature:
Manifold: tangent space, exp, log, dist, inner
Every manifold type implements Manifold. The remaining traits are optional
extensions:
Retraction: first-order retract replacing exp for cheap
optimization steps. A blanket implementation is provided via Manifold::exp.
Connection: covariant_derivative and christoffel_symbols.
Required by Curvature.
ParallelTransport: exact parallel_transport along geodesics.
Automatically provides VectorTransport via a blanket impl.
VectorTransport: vector_transport, the approximate analogue.
Cheaper than ParallelTransport; used in Riemannian Adam and conjugate gradient.
Curvature: sectional, ricci_tensor, and scalar_curvature.
Requires Connection.
The diagram on the home page shows the dependency arrows at a glance. Visit Concepts → for full mathematical definitions.
Crate Structure
cartan is a thin meta-crate that re-exports from focused sub-crates:
| Sub-crate | Contents | no_std? |
|---|---|---|
cartan-core | Trait definitions and const-generic primitives | yes (bare) |
cartan-manifolds | Sphere, Euclidean, SO(N), SE(N), SPD, Grassmann, Q-tensor | alloc feature |
cartan-optim | Riemannian gradient descent, Adam, conjugate gradient | alloc feature |
cartan-geo | Geodesic sampling, Fréchet mean, Riemannian interpolation | alloc feature |
cartan-dec | Discrete exterior calculus: Hodge star, exterior derivative | no (requires std) |
You can depend on individual sub-crates to avoid pulling in unused code:
[dependencies]
cartan-core = "0.1"
cartan-manifolds = "0.1"Embedded and no_std Targets
cartan-core, cartan-manifolds, cartan-optim, and cartan-geo all compile
on no_std targets with a global allocator. The cartan facade exposes three
feature tiers:
| Feature | What it enables | Requires |
|---|---|---|
full (default) | Everything including cartan-dec | std |
std | All geometry and optimization layers | std |
alloc | Same as std minus eigendecomposition-dependent types | global allocator |
# Embedded with allocator (most bare-metal targets):
cartan = { version = "0.1", default-features = false, features = ["alloc"] }
# std build without the mesh/PDE layer:
cartan = { version = "0.1", default-features = false, features = ["std"] }Types unavailable under alloc (require std): Spd<N>, Corr<N>, QTensor3,
FrameField, DisclincationScanner. All other manifolds and optimizers are available.
cartan-dec has no no_std support and is not planned to gain it; exclude it by
avoiding the full feature as shown above.