cartan 0.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cartan-0.1.7/Cargo.toml +36 -0
- cartan-0.1.7/PKG-INFO +16 -0
- cartan-0.1.7/cartan-core/Cargo.toml +27 -0
- cartan-0.1.7/cartan-core/src/connection.rs +73 -0
- cartan-0.1.7/cartan-core/src/curvature.rs +142 -0
- cartan-0.1.7/cartan-core/src/error.rs +134 -0
- cartan-0.1.7/cartan-core/src/geodesic.rs +76 -0
- cartan-0.1.7/cartan-core/src/lib.rs +58 -0
- cartan-0.1.7/cartan-core/src/manifold.rs +193 -0
- cartan-0.1.7/cartan-core/src/retraction.rs +68 -0
- cartan-0.1.7/cartan-core/src/transport.rs +128 -0
- cartan-0.1.7/cartan-dec/Cargo.toml +24 -0
- cartan-0.1.7/cartan-dec/src/advection.rs +125 -0
- cartan-0.1.7/cartan-dec/src/divergence.rs +143 -0
- cartan-0.1.7/cartan-dec/src/error.rs +35 -0
- cartan-0.1.7/cartan-dec/src/exterior.rs +79 -0
- cartan-0.1.7/cartan-dec/src/hodge.rs +120 -0
- cartan-0.1.7/cartan-dec/src/laplace.rs +301 -0
- cartan-0.1.7/cartan-dec/src/lib.rs +58 -0
- cartan-0.1.7/cartan-dec/src/mesh.rs +366 -0
- cartan-0.1.7/cartan-dec/tests/integration.rs +330 -0
- cartan-0.1.7/cartan-geo/Cargo.toml +30 -0
- cartan-0.1.7/cartan-geo/src/curvature.rs +74 -0
- cartan-0.1.7/cartan-geo/src/disclination/events.rs +233 -0
- cartan-0.1.7/cartan-geo/src/disclination/lines.rs +375 -0
- cartan-0.1.7/cartan-geo/src/disclination/mod.rs +10 -0
- cartan-0.1.7/cartan-geo/src/disclination/segments.rs +360 -0
- cartan-0.1.7/cartan-geo/src/geodesic.rs +126 -0
- cartan-0.1.7/cartan-geo/src/holonomy.rs +410 -0
- cartan-0.1.7/cartan-geo/src/jacobi.rs +167 -0
- cartan-0.1.7/cartan-geo/src/lib.rs +57 -0
- cartan-0.1.7/cartan-geo/tests/integration.rs +216 -0
- cartan-0.1.7/cartan-manifolds/Cargo.toml +33 -0
- cartan-0.1.7/cartan-manifolds/src/corr.rs +856 -0
- cartan-0.1.7/cartan-manifolds/src/euclidean.rs +246 -0
- cartan-0.1.7/cartan-manifolds/src/frame_field.rs +377 -0
- cartan-0.1.7/cartan-manifolds/src/grassmann.rs +831 -0
- cartan-0.1.7/cartan-manifolds/src/lib.rs +69 -0
- cartan-0.1.7/cartan-manifolds/src/qtensor.rs +711 -0
- cartan-0.1.7/cartan-manifolds/src/se.rs +1682 -0
- cartan-0.1.7/cartan-manifolds/src/so.rs +1947 -0
- cartan-0.1.7/cartan-manifolds/src/spd.rs +846 -0
- cartan-0.1.7/cartan-manifolds/src/sphere.rs +482 -0
- cartan-0.1.7/cartan-manifolds/src/util/matrix_exp.rs +1010 -0
- cartan-0.1.7/cartan-manifolds/src/util/matrix_log.rs +673 -0
- cartan-0.1.7/cartan-manifolds/src/util/mod.rs +42 -0
- cartan-0.1.7/cartan-manifolds/src/util/skew.rs +244 -0
- cartan-0.1.7/cartan-manifolds/src/util/sym.rs +340 -0
- cartan-0.1.7/cartan-optim/Cargo.toml +31 -0
- cartan-0.1.7/cartan-optim/src/frechet.rs +159 -0
- cartan-0.1.7/cartan-optim/src/lib.rs +60 -0
- cartan-0.1.7/cartan-optim/src/rcg.rs +206 -0
- cartan-0.1.7/cartan-optim/src/result.rs +25 -0
- cartan-0.1.7/cartan-optim/src/rgd.rs +137 -0
- cartan-0.1.7/cartan-optim/src/rtr.rs +272 -0
- cartan-0.1.7/cartan-optim/tests/integration.rs +359 -0
- cartan-0.1.7/cartan-py/Cargo.lock +651 -0
- cartan-0.1.7/cartan-py/Cargo.toml +27 -0
- cartan-0.1.7/cartan-py/src/convert.rs +152 -0
- cartan-0.1.7/cartan-py/src/dec/mesh.rs +194 -0
- cartan-0.1.7/cartan-py/src/dec/mod.rs +23 -0
- cartan-0.1.7/cartan-py/src/dec/operators.rs +450 -0
- cartan-0.1.7/cartan-py/src/error.rs +98 -0
- cartan-0.1.7/cartan-py/src/geo.rs +1037 -0
- cartan-0.1.7/cartan-py/src/holonomy.rs +277 -0
- cartan-0.1.7/cartan-py/src/lib.rs +22 -0
- cartan-0.1.7/cartan-py/src/manifolds/corr.rs +47 -0
- cartan-0.1.7/cartan-py/src/manifolds/euclidean.rs +44 -0
- cartan-0.1.7/cartan-py/src/manifolds/frame_field.rs +85 -0
- cartan-0.1.7/cartan-py/src/manifolds/grassmann.rs +509 -0
- cartan-0.1.7/cartan-py/src/manifolds/macros.rs +1086 -0
- cartan-0.1.7/cartan-py/src/manifolds/mod.rs +35 -0
- cartan-0.1.7/cartan-py/src/manifolds/qtensor.rs +388 -0
- cartan-0.1.7/cartan-py/src/manifolds/se.rs +587 -0
- cartan-0.1.7/cartan-py/src/manifolds/so.rs +48 -0
- cartan-0.1.7/cartan-py/src/manifolds/spd.rs +48 -0
- cartan-0.1.7/cartan-py/src/manifolds/sphere.rs +48 -0
- cartan-0.1.7/cartan-py/src/optim.rs +1152 -0
- cartan-0.1.7/cartan-py/tests/conftest.py +22 -0
- cartan-0.1.7/cartan-py/tests/test_batch.py +125 -0
- cartan-0.1.7/cartan-py/tests/test_corr.py +125 -0
- cartan-0.1.7/cartan-py/tests/test_dec.py +331 -0
- cartan-0.1.7/cartan-py/tests/test_euclidean.py +171 -0
- cartan-0.1.7/cartan-py/tests/test_geo.py +243 -0
- cartan-0.1.7/cartan-py/tests/test_grassmann.py +112 -0
- cartan-0.1.7/cartan-py/tests/test_holonomy.py +136 -0
- cartan-0.1.7/cartan-py/tests/test_optim.py +223 -0
- cartan-0.1.7/cartan-py/tests/test_properties.py +76 -0
- cartan-0.1.7/cartan-py/tests/test_qtensor.py +105 -0
- cartan-0.1.7/cartan-py/tests/test_se.py +227 -0
- cartan-0.1.7/cartan-py/tests/test_so.py +125 -0
- cartan-0.1.7/cartan-py/tests/test_spd.py +236 -0
- cartan-0.1.7/cartan-py/tests/test_sphere.py +246 -0
- cartan-0.1.7/pyproject.toml +25 -0
cartan-0.1.7/Cargo.toml
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
resolver = "2"
|
|
3
|
+
members = [
|
|
4
|
+
"cartan",
|
|
5
|
+
"cartan-core",
|
|
6
|
+
"cartan-manifolds",
|
|
7
|
+
"cartan-optim",
|
|
8
|
+
"cartan-geo",
|
|
9
|
+
"cartan-dec",
|
|
10
|
+
]
|
|
11
|
+
exclude = ["experiments", "cartan-py"]
|
|
12
|
+
|
|
13
|
+
[workspace.package]
|
|
14
|
+
version = "0.1.7"
|
|
15
|
+
edition = "2024"
|
|
16
|
+
rust-version = "1.85"
|
|
17
|
+
license = "MIT"
|
|
18
|
+
repository = "https://github.com/alejandro-soto-franco/cartan"
|
|
19
|
+
keywords = ["riemannian", "manifold", "optimization", "geometry", "geodesic"]
|
|
20
|
+
categories = ["mathematics", "science", "algorithms"]
|
|
21
|
+
|
|
22
|
+
[workspace.dependencies]
|
|
23
|
+
nalgebra = { version = "0.33", default-features = false }
|
|
24
|
+
rand = { version = "0.9", default-features = false }
|
|
25
|
+
rand_distr = { version = "0.5", default-features = false }
|
|
26
|
+
rayon = { version = "1" }
|
|
27
|
+
thiserror = { version = "2", default-features = false }
|
|
28
|
+
serde = { version = "1", features = ["derive"], default-features = false }
|
|
29
|
+
serde_json = { version = "1", default-features = false }
|
|
30
|
+
approx = { version = "0.5", default-features = false }
|
|
31
|
+
hashbrown = { version = "0.15", default-features = false }
|
|
32
|
+
|
|
33
|
+
[profile.release]
|
|
34
|
+
opt-level = 3
|
|
35
|
+
lto = "thin" # fat LTO was causing OOM on 30 GB machine; thin cuts linker peak ~5-10x
|
|
36
|
+
codegen-units = 4 # default is 16; lower = less parallel RAM, better optimization
|
cartan-0.1.7/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cartan
|
|
3
|
+
Version: 0.1.7
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
7
|
+
Requires-Dist: numpy>=1.20
|
|
8
|
+
Requires-Dist: scipy>=1.7 ; extra == 'dec'
|
|
9
|
+
Requires-Dist: pytest ; extra == 'dev'
|
|
10
|
+
Requires-Dist: hypothesis ; extra == 'dev'
|
|
11
|
+
Requires-Dist: scipy>=1.7 ; extra == 'dev'
|
|
12
|
+
Provides-Extra: dec
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Summary: Python bindings for the cartan Riemannian geometry library
|
|
15
|
+
License: MIT
|
|
16
|
+
Requires-Python: >=3.9
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "cartan-core"
|
|
3
|
+
description = "Core traits for Riemannian geometry: Manifold, Retraction, ParallelTransport, Curvature"
|
|
4
|
+
version.workspace = true
|
|
5
|
+
edition.workspace = true
|
|
6
|
+
rust-version.workspace = true
|
|
7
|
+
license.workspace = true
|
|
8
|
+
repository.workspace = true
|
|
9
|
+
keywords = ["riemannian", "manifold", "geometry", "traits", "geodesic"]
|
|
10
|
+
categories = ["mathematics", "science", "algorithms"]
|
|
11
|
+
documentation = "https://docs.rs/cartan-core"
|
|
12
|
+
homepage = "https://cartan.sotofranco.dev"
|
|
13
|
+
|
|
14
|
+
[features]
|
|
15
|
+
default = ["std"]
|
|
16
|
+
std = ["alloc", "rand/std"]
|
|
17
|
+
alloc = ["rand/alloc"]
|
|
18
|
+
|
|
19
|
+
[dependencies]
|
|
20
|
+
rand = { workspace = true, default-features = false }
|
|
21
|
+
# libm provides `sqrt` and other float math in no_std environments.
|
|
22
|
+
# Always present; only used when `std` feature is disabled.
|
|
23
|
+
libm = { version = "0.2", default-features = false }
|
|
24
|
+
|
|
25
|
+
[package.metadata.docs.rs]
|
|
26
|
+
all-features = true
|
|
27
|
+
rustdoc-args = ["--cfg", "docsrs"]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// ~/cartan/cartan-core/src/connection.rs
|
|
2
|
+
|
|
3
|
+
//! The `Connection` trait: Riemannian Hessian-vector products.
|
|
4
|
+
//!
|
|
5
|
+
//! The Levi-Civita connection on a Riemannian manifold provides a notion
|
|
6
|
+
//! of "derivative of a vector field along a curve." For optimization, the
|
|
7
|
+
//! key application is computing the Riemannian Hessian of a cost function,
|
|
8
|
+
//! or more precisely, Hessian-vector products (HVPs) needed by second-order
|
|
9
|
+
//! methods like Riemannian trust region and Riemannian Newton.
|
|
10
|
+
//!
|
|
11
|
+
//! ## Hessian-vector products
|
|
12
|
+
//!
|
|
13
|
+
//! Given a smooth cost function f: M -> R, the Riemannian Hessian at p
|
|
14
|
+
//! is a symmetric bilinear form on T_pM. For trust region methods, we need
|
|
15
|
+
//! H\[v\] = Hess f(p)\[v\], the action of the Hessian on a tangent vector v.
|
|
16
|
+
//!
|
|
17
|
+
//! For a Riemannian manifold embedded in Euclidean space, the Riemannian HVP
|
|
18
|
+
//! can be computed from the Euclidean Hessian as:
|
|
19
|
+
//!
|
|
20
|
+
//! Hess f(p)\[v\] = proj_T( D^2 f(p)\[v\] - II(grad f, v) )
|
|
21
|
+
//!
|
|
22
|
+
//! where II is the second fundamental form (shape operator), and proj_T is
|
|
23
|
+
//! the projection onto the tangent space.
|
|
24
|
+
//!
|
|
25
|
+
//! Alternatively, for cost functions defined via retraction, one uses the
|
|
26
|
+
//! differentiated retraction formula from Absil-Mahony-Sepulchre Ch. 5.
|
|
27
|
+
//!
|
|
28
|
+
//! ## References
|
|
29
|
+
//!
|
|
30
|
+
//! - Absil, Mahony, Sepulchre. "Optimization Algorithms on Matrix Manifolds."
|
|
31
|
+
//! Princeton, 2008. Chapter 5 (Riemannian Hessian and trust region).
|
|
32
|
+
//! - Boumal. "An Introduction to Optimization on Smooth Manifolds."
|
|
33
|
+
//! Cambridge, 2023. Chapter 6 (connections and Hessian).
|
|
34
|
+
//! - do Carmo. "Riemannian Geometry." Chapter 2 (Levi-Civita connection).
|
|
35
|
+
|
|
36
|
+
use crate::{CartanError, Manifold};
|
|
37
|
+
|
|
38
|
+
/// A manifold with a compatible Levi-Civita connection.
|
|
39
|
+
///
|
|
40
|
+
/// Implement this trait to enable second-order optimization methods
|
|
41
|
+
/// (trust region, Newton) that require Riemannian Hessian-vector products.
|
|
42
|
+
///
|
|
43
|
+
/// # Supertraiting Manifold
|
|
44
|
+
///
|
|
45
|
+
/// Connection requires Self: Manifold to ensure that the manifold has
|
|
46
|
+
/// the full geometric structure (exp, log, inner, project) before adding
|
|
47
|
+
/// the connection. The Levi-Civita connection depends on the metric, so
|
|
48
|
+
/// this dependency is semantically required, not just a convenience.
|
|
49
|
+
pub trait Connection: Manifold {
|
|
50
|
+
/// Riemannian Hessian-vector product: Hess f(p)\[v\].
|
|
51
|
+
///
|
|
52
|
+
/// Given:
|
|
53
|
+
/// - p: a point on the manifold
|
|
54
|
+
/// - grad_f: the Riemannian gradient of f at p (a tangent vector in T\_pM)
|
|
55
|
+
/// - v: a tangent vector in T\_pM (the direction for the HVP)
|
|
56
|
+
/// - hess_ambient: a callback that computes the Euclidean Hessian-vector
|
|
57
|
+
/// product H\_eucl\[v\] = D^2 f(p)\[v\] in ambient coordinates
|
|
58
|
+
///
|
|
59
|
+
/// Returns Hess f(p)\[v\] in T\_pM.
|
|
60
|
+
///
|
|
61
|
+
/// The `hess_ambient` callback takes a tangent vector in ambient coordinates
|
|
62
|
+
/// and returns the ambient Euclidean HVP. This avoids the need to explicitly
|
|
63
|
+
/// construct the full Hessian matrix (which is expensive for large N).
|
|
64
|
+
///
|
|
65
|
+
/// Ref: Absil-Mahony-Sepulchre, Chapter 5, Proposition 5.3.2.
|
|
66
|
+
fn riemannian_hessian_vector_product(
|
|
67
|
+
&self,
|
|
68
|
+
p: &Self::Point,
|
|
69
|
+
grad_f: &Self::Tangent,
|
|
70
|
+
v: &Self::Tangent,
|
|
71
|
+
hess_ambient: &dyn Fn(&Self::Tangent) -> Self::Tangent,
|
|
72
|
+
) -> Result<Self::Tangent, CartanError>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// ~/cartan/cartan-core/src/curvature.rs
|
|
2
|
+
|
|
3
|
+
//! The `Curvature` trait: Riemann curvature tensor and derived quantities.
|
|
4
|
+
//!
|
|
5
|
+
//! Riemannian curvature measures how much the manifold deviates from being
|
|
6
|
+
//! flat. It appears in:
|
|
7
|
+
//! - Geodesic deviation (Jacobi equation)
|
|
8
|
+
//! - Error bounds for optimization algorithms (convergence rates depend on
|
|
9
|
+
//! sectional curvature bounds)
|
|
10
|
+
//! - Comparison geometry (Toponogov's theorem, Bishop-Gromov volume comparison)
|
|
11
|
+
//!
|
|
12
|
+
//! ## Curvature quantities
|
|
13
|
+
//!
|
|
14
|
+
//! Starting from the Riemann curvature tensor R(X, Y)Z (a (1,3)-tensor),
|
|
15
|
+
//! one derives:
|
|
16
|
+
//!
|
|
17
|
+
//! - **Sectional curvature** K(sigma) for a 2-plane sigma = span{X, Y}:
|
|
18
|
+
//! K(sigma) = <R(X,Y)Y, X> / (<X,X><Y,Y> - <X,Y>^2).
|
|
19
|
+
//! Controls geodesic spreading: K > 0 makes geodesics converge (sphere),
|
|
20
|
+
//! K < 0 makes them diverge (hyperbolic space), K = 0 flat (Euclidean).
|
|
21
|
+
//!
|
|
22
|
+
//! - **Ricci curvature** Ric(X, Y): trace of Z -> R(Z, X)Y.
|
|
23
|
+
//! Appears in volume comparison and Ricci flow.
|
|
24
|
+
//!
|
|
25
|
+
//! - **Scalar curvature** s: trace of the Ricci tensor.
|
|
26
|
+
//!
|
|
27
|
+
//! ## Known values for v0.1 manifolds
|
|
28
|
+
//!
|
|
29
|
+
//! - Sphere S^{N-1}: K = 1 (constant sectional curvature)
|
|
30
|
+
//! - Hyperbolic H^N: K = -1 (constant sectional curvature)
|
|
31
|
+
//! - SO(N) (bi-invariant): K(X,Y) = (1/4)||\[X,Y\]||^2 / (||X||^2 ||Y||^2 - <X,Y>^2)
|
|
32
|
+
//! - SPD(N) (affine-invariant): K <= 0 (non-positive, Cartan-Hadamard manifold)
|
|
33
|
+
//! - Grassmann Gr(n,k): 0 <= K <= 2 (between 0 and 2)
|
|
34
|
+
//! - Euclidean R^N: K = 0 everywhere
|
|
35
|
+
//!
|
|
36
|
+
//! ## References
|
|
37
|
+
//!
|
|
38
|
+
//! - do Carmo. "Riemannian Geometry." Chapter 4 (curvature tensor), Chapter 5 (sectional).
|
|
39
|
+
//! - Milnor. "Curvatures of Left-Invariant Metrics on Lie Groups." Advances in Math, 1976.
|
|
40
|
+
//! - Bhatia. "Positive Definite Matrices." Princeton, 2007. Chapter 6 (curvature of SPD).
|
|
41
|
+
|
|
42
|
+
use crate::Real;
|
|
43
|
+
use crate::connection::Connection;
|
|
44
|
+
|
|
45
|
+
/// A manifold with explicitly computable Riemannian curvature.
|
|
46
|
+
///
|
|
47
|
+
/// This trait provides the Riemann curvature tensor and derived scalar
|
|
48
|
+
/// quantities. It is useful for:
|
|
49
|
+
/// - Theoretical analysis (verifying curvature bounds for convergence proofs)
|
|
50
|
+
/// - Geodesic deviation simulation (Jacobi fields)
|
|
51
|
+
/// - Visualization (sectional curvature plots)
|
|
52
|
+
///
|
|
53
|
+
/// # Supertraiting Connection
|
|
54
|
+
///
|
|
55
|
+
/// Curvature requires Self: Connection because the curvature tensor is defined
|
|
56
|
+
/// via the Levi-Civita connection: R(X,Y)Z = nabla_X nabla_Y Z - nabla_Y nabla_X Z
|
|
57
|
+
/// - nabla_{\[X,Y\]} Z. Without the connection, curvature cannot be defined.
|
|
58
|
+
pub trait Curvature: Connection {
|
|
59
|
+
/// The Riemann curvature tensor: R(u, v)w.
|
|
60
|
+
///
|
|
61
|
+
/// Takes three tangent vectors u, v, w at p and returns a tangent vector
|
|
62
|
+
/// at p. Defined via the Levi-Civita connection as:
|
|
63
|
+
///
|
|
64
|
+
/// R(u,v)w = nabla_u nabla_v w - nabla_v nabla_u w - nabla_{\[u,v\]} w
|
|
65
|
+
///
|
|
66
|
+
/// Properties:
|
|
67
|
+
/// - Anti-symmetry: R(u,v)w = -R(v,u)w
|
|
68
|
+
/// - First Bianchi identity: R(u,v)w + R(v,w)u + R(w,u)v = 0
|
|
69
|
+
/// - Symmetry: <R(u,v)w, z> = <R(w,z)u, v>
|
|
70
|
+
///
|
|
71
|
+
/// Ref: do Carmo, Chapter 4, Definition 1.
|
|
72
|
+
fn riemann_curvature(
|
|
73
|
+
&self,
|
|
74
|
+
p: &Self::Point,
|
|
75
|
+
u: &Self::Tangent,
|
|
76
|
+
v: &Self::Tangent,
|
|
77
|
+
w: &Self::Tangent,
|
|
78
|
+
) -> Self::Tangent;
|
|
79
|
+
|
|
80
|
+
/// Sectional curvature of the 2-plane spanned by u and v at p.
|
|
81
|
+
///
|
|
82
|
+
/// K(u, v) = <R(u,v)v, u>_p / (<u,u>_p <v,v>_p - <u,v>_p^2)
|
|
83
|
+
///
|
|
84
|
+
/// The denominator is the squared area of the parallelogram spanned by u, v.
|
|
85
|
+
/// This quantity is well-defined only when u and v are linearly independent.
|
|
86
|
+
///
|
|
87
|
+
/// The default implementation computes this from riemann_curvature() and inner().
|
|
88
|
+
/// Manifolds with closed-form sectional curvature (sphere, hyperbolic) should
|
|
89
|
+
/// override this with a direct formula to avoid the overhead of computing the
|
|
90
|
+
/// full curvature tensor.
|
|
91
|
+
fn sectional_curvature(&self, p: &Self::Point, u: &Self::Tangent, v: &Self::Tangent) -> Real {
|
|
92
|
+
// Numerator: <R(u,v)v, u>_p
|
|
93
|
+
// This is the standard formula for sectional curvature.
|
|
94
|
+
let r_uvv = self.riemann_curvature(p, u, v, v);
|
|
95
|
+
let numerator = self.inner(p, u, &r_uvv);
|
|
96
|
+
|
|
97
|
+
// Denominator: ||u||^2 ||v||^2 - <u,v>^2
|
|
98
|
+
// This is the squared area of the parallelogram (= 0 iff u,v are parallel).
|
|
99
|
+
let uu = self.inner(p, u, u);
|
|
100
|
+
let vv = self.inner(p, v, v);
|
|
101
|
+
let uv = self.inner(p, u, v);
|
|
102
|
+
let denominator = uu * vv - uv * uv;
|
|
103
|
+
|
|
104
|
+
// Guard against degenerate planes: if u and v are (nearly) parallel,
|
|
105
|
+
// the 2-plane is degenerate and sectional curvature is undefined.
|
|
106
|
+
// Return 0.0 instead of NaN/Inf to avoid poisoning downstream computations.
|
|
107
|
+
// Callers who need to distinguish this case should check the denominator
|
|
108
|
+
// (area of the parallelogram) before calling sectional_curvature.
|
|
109
|
+
if denominator.abs() < 1e-20 {
|
|
110
|
+
return 0.0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
numerator / denominator
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// Ricci curvature tensor applied to tangent vectors u and v: Ric(u, v).
|
|
117
|
+
///
|
|
118
|
+
/// The Ricci tensor is the trace of the full curvature tensor:
|
|
119
|
+
/// Ric(u, v) = sum_{i=1}^{n} <R(e_i, u)v, e_i>
|
|
120
|
+
/// where {e_i} is an orthonormal basis for T_p M.
|
|
121
|
+
///
|
|
122
|
+
/// Implementations should compute this directly for each manifold
|
|
123
|
+
/// rather than summing over an explicit basis (which requires O(n)
|
|
124
|
+
/// curvature evaluations).
|
|
125
|
+
///
|
|
126
|
+
/// Known values:
|
|
127
|
+
/// - Sphere S^{N-1}: Ric(u,v) = (N-2) <u,v>
|
|
128
|
+
/// - SO(N): Ric(u,v) = (N-2)/4 * <u,v> (for N >= 3)
|
|
129
|
+
/// - Euclidean: Ric = 0
|
|
130
|
+
fn ricci_curvature(&self, p: &Self::Point, u: &Self::Tangent, v: &Self::Tangent) -> Real;
|
|
131
|
+
|
|
132
|
+
/// Scalar curvature at p.
|
|
133
|
+
///
|
|
134
|
+
/// The scalar curvature is the trace of the Ricci tensor:
|
|
135
|
+
/// s(p) = sum_{i=1}^{n} Ric(e_i, e_i)
|
|
136
|
+
/// where {e_i} is an orthonormal basis for T_p M.
|
|
137
|
+
///
|
|
138
|
+
/// Known values (dimension n):
|
|
139
|
+
/// - Sphere S^{N-1} (n = N-1): s = (N-1)(N-2)
|
|
140
|
+
/// - Euclidean: s = 0
|
|
141
|
+
fn scalar_curvature(&self, p: &Self::Point) -> Real;
|
|
142
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// ~/cartan/cartan-core/src/error.rs
|
|
2
|
+
|
|
3
|
+
//! Error types for cartan operations.
|
|
4
|
+
//!
|
|
5
|
+
//! CartanError covers all failure modes in Riemannian geometry computations:
|
|
6
|
+
//! logarithmic map failures at cut loci, numerical breakdowns in matrix
|
|
7
|
+
//! decompositions, constraint violations, and optimizer convergence failures.
|
|
8
|
+
|
|
9
|
+
use core::fmt;
|
|
10
|
+
|
|
11
|
+
use crate::Real;
|
|
12
|
+
|
|
13
|
+
/// The unified error type for all cartan operations.
|
|
14
|
+
///
|
|
15
|
+
/// Each variant captures a specific class of failure with enough context
|
|
16
|
+
/// to diagnose the problem. Mathematical operations that can fail (log map,
|
|
17
|
+
/// parallel transport across cut locus, Cholesky of near-singular matrix)
|
|
18
|
+
/// return Result<T, CartanError>.
|
|
19
|
+
///
|
|
20
|
+
/// Under `no_alloc`, message fields are `&'static str` (no heap).
|
|
21
|
+
/// Under `alloc` or `std`, message fields are `String` (rich formatting).
|
|
22
|
+
#[derive(Debug, Clone)]
|
|
23
|
+
pub enum CartanError {
|
|
24
|
+
/// Log map failed: point is on or near the cut locus.
|
|
25
|
+
///
|
|
26
|
+
/// On the sphere, this means the two points are nearly antipodal.
|
|
27
|
+
/// On SO(n), this means the rotation angle is near pi.
|
|
28
|
+
/// On Cartan-Hadamard manifolds (SPD, Hyperbolic), this should
|
|
29
|
+
/// never occur since the cut locus is empty.
|
|
30
|
+
CutLocus {
|
|
31
|
+
#[cfg(feature = "alloc")]
|
|
32
|
+
message: alloc::string::String,
|
|
33
|
+
#[cfg(not(feature = "alloc"))]
|
|
34
|
+
message: &'static str,
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/// A matrix decomposition or numerical computation failed.
|
|
38
|
+
///
|
|
39
|
+
/// Examples: Cholesky on a matrix that lost positive-definiteness
|
|
40
|
+
/// due to roundoff, SVD that did not converge, matrix logarithm
|
|
41
|
+
/// of a matrix with negative eigenvalues.
|
|
42
|
+
NumericalFailure {
|
|
43
|
+
#[cfg(feature = "alloc")]
|
|
44
|
+
operation: alloc::string::String,
|
|
45
|
+
#[cfg(feature = "alloc")]
|
|
46
|
+
message: alloc::string::String,
|
|
47
|
+
#[cfg(not(feature = "alloc"))]
|
|
48
|
+
operation: &'static str,
|
|
49
|
+
#[cfg(not(feature = "alloc"))]
|
|
50
|
+
message: &'static str,
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/// Point does not satisfy the manifold constraint.
|
|
54
|
+
///
|
|
55
|
+
/// The `constraint` field describes what was checked (e.g., "||p|| = 1"
|
|
56
|
+
/// for the sphere), and `violation` gives the magnitude of the deviation.
|
|
57
|
+
NotOnManifold {
|
|
58
|
+
#[cfg(feature = "alloc")]
|
|
59
|
+
constraint: alloc::string::String,
|
|
60
|
+
#[cfg(not(feature = "alloc"))]
|
|
61
|
+
constraint: &'static str,
|
|
62
|
+
violation: Real,
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/// Tangent vector is not in the tangent space at the given point.
|
|
66
|
+
///
|
|
67
|
+
/// The `constraint` field describes the tangent space condition
|
|
68
|
+
/// (e.g., "p^T v = 0" for the sphere).
|
|
69
|
+
NotInTangentSpace {
|
|
70
|
+
#[cfg(feature = "alloc")]
|
|
71
|
+
constraint: alloc::string::String,
|
|
72
|
+
#[cfg(not(feature = "alloc"))]
|
|
73
|
+
constraint: &'static str,
|
|
74
|
+
violation: Real,
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/// Line search failed to find a step size satisfying the Armijo condition.
|
|
78
|
+
LineSearchFailed { steps_tried: usize },
|
|
79
|
+
|
|
80
|
+
/// Optimizer did not converge within the maximum number of iterations.
|
|
81
|
+
ConvergenceFailure {
|
|
82
|
+
iterations: usize,
|
|
83
|
+
gradient_norm: Real,
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
impl fmt::Display for CartanError {
|
|
88
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
89
|
+
match self {
|
|
90
|
+
CartanError::CutLocus { message } => {
|
|
91
|
+
write!(f, "cut locus: {}", message)
|
|
92
|
+
}
|
|
93
|
+
CartanError::NumericalFailure { operation, message } => {
|
|
94
|
+
write!(f, "numerical failure in {}: {}", operation, message)
|
|
95
|
+
}
|
|
96
|
+
CartanError::NotOnManifold {
|
|
97
|
+
constraint,
|
|
98
|
+
violation,
|
|
99
|
+
} => {
|
|
100
|
+
write!(
|
|
101
|
+
f,
|
|
102
|
+
"point not on manifold: {} violated by {}",
|
|
103
|
+
constraint, violation
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
CartanError::NotInTangentSpace {
|
|
107
|
+
constraint,
|
|
108
|
+
violation,
|
|
109
|
+
} => {
|
|
110
|
+
write!(
|
|
111
|
+
f,
|
|
112
|
+
"tangent vector not in tangent space: {} violated by {}",
|
|
113
|
+
constraint, violation
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
CartanError::LineSearchFailed { steps_tried } => {
|
|
117
|
+
write!(f, "line search failed after {} steps", steps_tried)
|
|
118
|
+
}
|
|
119
|
+
CartanError::ConvergenceFailure {
|
|
120
|
+
iterations,
|
|
121
|
+
gradient_norm,
|
|
122
|
+
} => {
|
|
123
|
+
write!(
|
|
124
|
+
f,
|
|
125
|
+
"optimizer did not converge after {} iterations (gradient norm: {:.2e})",
|
|
126
|
+
iterations, gradient_norm
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#[cfg(feature = "std")]
|
|
134
|
+
impl std::error::Error for CartanError {}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// ~/cartan/cartan-core/src/geodesic.rs
|
|
2
|
+
|
|
3
|
+
//! The `GeodesicInterpolation` trait: sampling points along geodesics.
|
|
4
|
+
//!
|
|
5
|
+
//! Geodesic interpolation provides a way to move continuously between
|
|
6
|
+
//! two points on a manifold along the shortest path (minimizing geodesic).
|
|
7
|
+
//! This is the manifold analogue of linear interpolation in Euclidean space.
|
|
8
|
+
//!
|
|
9
|
+
//! ## Definition
|
|
10
|
+
//!
|
|
11
|
+
//! Given points p, q in M and parameter t in [0, 1], the geodesic
|
|
12
|
+
//! interpolation gamma(p, q, t) is defined as:
|
|
13
|
+
//!
|
|
14
|
+
//! gamma(p, q, t) = Exp_p(t * Log_p(q))
|
|
15
|
+
//!
|
|
16
|
+
//! Properties:
|
|
17
|
+
//! gamma(p, q, 0) = p
|
|
18
|
+
//! gamma(p, q, 1) = q
|
|
19
|
+
//! d/dt gamma(p, q, t)|_{t=0} = Log_p(q) (initial velocity)
|
|
20
|
+
//!
|
|
21
|
+
//! ## Applications
|
|
22
|
+
//!
|
|
23
|
+
//! - Frechet mean computation (iterative: barycenter in T_pM)
|
|
24
|
+
//! - Geodesic grid generation for visualization
|
|
25
|
+
//! - Midpoint computation (t = 0.5) for Riemannian bisection
|
|
26
|
+
//! - Slerp generalization (spherical: reduces to standard slerp)
|
|
27
|
+
//!
|
|
28
|
+
//! ## Failure modes
|
|
29
|
+
//!
|
|
30
|
+
//! Like Log_p(q), geodesic interpolation fails at the cut locus. The
|
|
31
|
+
//! method returns an error rather than panicking, allowing callers to
|
|
32
|
+
//! detect and handle this case (e.g., by using a retraction-based fallback).
|
|
33
|
+
//!
|
|
34
|
+
//! ## References
|
|
35
|
+
//!
|
|
36
|
+
//! - do Carmo. "Riemannian Geometry." Chapter 3 (geodesics).
|
|
37
|
+
//! - Pennec, Fillard, Ayache. "A Riemannian Framework for Tensor Computing."
|
|
38
|
+
//! IJCV, 2006. Section 3.2 (geodesic interpolation for SPD matrices).
|
|
39
|
+
|
|
40
|
+
use crate::{CartanError, Manifold};
|
|
41
|
+
|
|
42
|
+
/// A manifold with geodesic interpolation between points.
|
|
43
|
+
///
|
|
44
|
+
/// The geodesic interpolation gamma(p, q, t) = Exp_p(t * Log_p(q))
|
|
45
|
+
/// parameterizes the unique minimizing geodesic from p to q (when it exists).
|
|
46
|
+
///
|
|
47
|
+
/// # Supertraiting Manifold
|
|
48
|
+
///
|
|
49
|
+
/// GeodesicInterpolation requires Self: Manifold because it is defined
|
|
50
|
+
/// via exp and log. A default implementation could be provided here using
|
|
51
|
+
/// self.exp(p, self.log(p, q)? * t), but we leave it as an abstract method
|
|
52
|
+
/// to allow manifolds to provide more numerically stable implementations
|
|
53
|
+
/// (e.g., direct formula on the sphere using sin/cos).
|
|
54
|
+
pub trait GeodesicInterpolation: Manifold {
|
|
55
|
+
/// Interpolate along the geodesic from p to q at parameter t.
|
|
56
|
+
///
|
|
57
|
+
/// For t = 0: returns p.
|
|
58
|
+
/// For t = 1: returns q.
|
|
59
|
+
/// For t = 0.5: returns the geodesic midpoint.
|
|
60
|
+
/// For t outside \[0,1\]: extrapolates along the geodesic (valid if
|
|
61
|
+
/// t * dist(p, q) < injectivity_radius(p)).
|
|
62
|
+
///
|
|
63
|
+
/// Fails if Log_p(q) fails (p and q are at or beyond the cut locus).
|
|
64
|
+
///
|
|
65
|
+
/// # Arguments
|
|
66
|
+
///
|
|
67
|
+
/// - `p`: starting point
|
|
68
|
+
/// - `q`: ending point
|
|
69
|
+
/// - `t`: interpolation parameter (0 = start, 1 = end, 0.5 = midpoint)
|
|
70
|
+
fn geodesic(
|
|
71
|
+
&self,
|
|
72
|
+
p: &Self::Point,
|
|
73
|
+
q: &Self::Point,
|
|
74
|
+
t: crate::Real,
|
|
75
|
+
) -> Result<Self::Point, CartanError>;
|
|
76
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// ~/cartan/cartan-core/src/lib.rs
|
|
2
|
+
|
|
3
|
+
//! # cartan-core
|
|
4
|
+
//!
|
|
5
|
+
//! Core trait definitions for Riemannian geometry.
|
|
6
|
+
//!
|
|
7
|
+
//! This crate defines the foundational traits that all cartan manifolds,
|
|
8
|
+
//! optimizers, and tools depend on. It has minimal dependencies (only `rand`
|
|
9
|
+
//! for the Rng trait bound) and can be used standalone by downstream crates
|
|
10
|
+
//! that want to implement custom manifolds against the cartan trait system.
|
|
11
|
+
//!
|
|
12
|
+
//! ## Trait hierarchy
|
|
13
|
+
//!
|
|
14
|
+
//! ```text
|
|
15
|
+
//! Manifold (base: exp, log, inner, project, validate)
|
|
16
|
+
//! |
|
|
17
|
+
//! +-- Retraction (cheaper exp approximation)
|
|
18
|
+
//! +-- ParallelTransport -> VectorTransport (blanket impl)
|
|
19
|
+
//! +-- Connection (Riemannian Hessian)
|
|
20
|
+
//! | |
|
|
21
|
+
//! | +-- Curvature (Riemann tensor, Ricci, scalar)
|
|
22
|
+
//! +-- GeodesicInterpolation (gamma(t) sampling)
|
|
23
|
+
//! ```
|
|
24
|
+
//!
|
|
25
|
+
//! ## The `Real` type alias
|
|
26
|
+
//!
|
|
27
|
+
//! All floating-point computation uses `Real`, currently aliased to `f64`.
|
|
28
|
+
//! This exists so that a future version can generify over `T: Scalar`
|
|
29
|
+
//! with a mechanical find-and-replace refactor.
|
|
30
|
+
|
|
31
|
+
#![cfg_attr(not(feature = "std"), no_std)]
|
|
32
|
+
|
|
33
|
+
#[cfg(feature = "alloc")]
|
|
34
|
+
extern crate alloc;
|
|
35
|
+
|
|
36
|
+
pub mod connection;
|
|
37
|
+
pub mod curvature;
|
|
38
|
+
pub mod error;
|
|
39
|
+
pub mod geodesic;
|
|
40
|
+
pub mod manifold;
|
|
41
|
+
pub mod retraction;
|
|
42
|
+
pub mod transport;
|
|
43
|
+
|
|
44
|
+
/// The floating-point type used throughout cartan.
|
|
45
|
+
///
|
|
46
|
+
/// Currently f64. Designed so that replacing this alias with a generic
|
|
47
|
+
/// type parameter `T: Scalar` is a mechanical refactor when f32 support
|
|
48
|
+
/// is needed. All structs and functions use `Real` instead of `f64` directly.
|
|
49
|
+
pub type Real = f64;
|
|
50
|
+
|
|
51
|
+
// Re-exports for convenience.
|
|
52
|
+
pub use connection::Connection;
|
|
53
|
+
pub use curvature::Curvature;
|
|
54
|
+
pub use error::CartanError;
|
|
55
|
+
pub use geodesic::GeodesicInterpolation;
|
|
56
|
+
pub use manifold::Manifold;
|
|
57
|
+
pub use retraction::Retraction;
|
|
58
|
+
pub use transport::{ParallelTransport, VectorTransport};
|