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.
Files changed (94) hide show
  1. cartan-0.1.7/Cargo.toml +36 -0
  2. cartan-0.1.7/PKG-INFO +16 -0
  3. cartan-0.1.7/cartan-core/Cargo.toml +27 -0
  4. cartan-0.1.7/cartan-core/src/connection.rs +73 -0
  5. cartan-0.1.7/cartan-core/src/curvature.rs +142 -0
  6. cartan-0.1.7/cartan-core/src/error.rs +134 -0
  7. cartan-0.1.7/cartan-core/src/geodesic.rs +76 -0
  8. cartan-0.1.7/cartan-core/src/lib.rs +58 -0
  9. cartan-0.1.7/cartan-core/src/manifold.rs +193 -0
  10. cartan-0.1.7/cartan-core/src/retraction.rs +68 -0
  11. cartan-0.1.7/cartan-core/src/transport.rs +128 -0
  12. cartan-0.1.7/cartan-dec/Cargo.toml +24 -0
  13. cartan-0.1.7/cartan-dec/src/advection.rs +125 -0
  14. cartan-0.1.7/cartan-dec/src/divergence.rs +143 -0
  15. cartan-0.1.7/cartan-dec/src/error.rs +35 -0
  16. cartan-0.1.7/cartan-dec/src/exterior.rs +79 -0
  17. cartan-0.1.7/cartan-dec/src/hodge.rs +120 -0
  18. cartan-0.1.7/cartan-dec/src/laplace.rs +301 -0
  19. cartan-0.1.7/cartan-dec/src/lib.rs +58 -0
  20. cartan-0.1.7/cartan-dec/src/mesh.rs +366 -0
  21. cartan-0.1.7/cartan-dec/tests/integration.rs +330 -0
  22. cartan-0.1.7/cartan-geo/Cargo.toml +30 -0
  23. cartan-0.1.7/cartan-geo/src/curvature.rs +74 -0
  24. cartan-0.1.7/cartan-geo/src/disclination/events.rs +233 -0
  25. cartan-0.1.7/cartan-geo/src/disclination/lines.rs +375 -0
  26. cartan-0.1.7/cartan-geo/src/disclination/mod.rs +10 -0
  27. cartan-0.1.7/cartan-geo/src/disclination/segments.rs +360 -0
  28. cartan-0.1.7/cartan-geo/src/geodesic.rs +126 -0
  29. cartan-0.1.7/cartan-geo/src/holonomy.rs +410 -0
  30. cartan-0.1.7/cartan-geo/src/jacobi.rs +167 -0
  31. cartan-0.1.7/cartan-geo/src/lib.rs +57 -0
  32. cartan-0.1.7/cartan-geo/tests/integration.rs +216 -0
  33. cartan-0.1.7/cartan-manifolds/Cargo.toml +33 -0
  34. cartan-0.1.7/cartan-manifolds/src/corr.rs +856 -0
  35. cartan-0.1.7/cartan-manifolds/src/euclidean.rs +246 -0
  36. cartan-0.1.7/cartan-manifolds/src/frame_field.rs +377 -0
  37. cartan-0.1.7/cartan-manifolds/src/grassmann.rs +831 -0
  38. cartan-0.1.7/cartan-manifolds/src/lib.rs +69 -0
  39. cartan-0.1.7/cartan-manifolds/src/qtensor.rs +711 -0
  40. cartan-0.1.7/cartan-manifolds/src/se.rs +1682 -0
  41. cartan-0.1.7/cartan-manifolds/src/so.rs +1947 -0
  42. cartan-0.1.7/cartan-manifolds/src/spd.rs +846 -0
  43. cartan-0.1.7/cartan-manifolds/src/sphere.rs +482 -0
  44. cartan-0.1.7/cartan-manifolds/src/util/matrix_exp.rs +1010 -0
  45. cartan-0.1.7/cartan-manifolds/src/util/matrix_log.rs +673 -0
  46. cartan-0.1.7/cartan-manifolds/src/util/mod.rs +42 -0
  47. cartan-0.1.7/cartan-manifolds/src/util/skew.rs +244 -0
  48. cartan-0.1.7/cartan-manifolds/src/util/sym.rs +340 -0
  49. cartan-0.1.7/cartan-optim/Cargo.toml +31 -0
  50. cartan-0.1.7/cartan-optim/src/frechet.rs +159 -0
  51. cartan-0.1.7/cartan-optim/src/lib.rs +60 -0
  52. cartan-0.1.7/cartan-optim/src/rcg.rs +206 -0
  53. cartan-0.1.7/cartan-optim/src/result.rs +25 -0
  54. cartan-0.1.7/cartan-optim/src/rgd.rs +137 -0
  55. cartan-0.1.7/cartan-optim/src/rtr.rs +272 -0
  56. cartan-0.1.7/cartan-optim/tests/integration.rs +359 -0
  57. cartan-0.1.7/cartan-py/Cargo.lock +651 -0
  58. cartan-0.1.7/cartan-py/Cargo.toml +27 -0
  59. cartan-0.1.7/cartan-py/src/convert.rs +152 -0
  60. cartan-0.1.7/cartan-py/src/dec/mesh.rs +194 -0
  61. cartan-0.1.7/cartan-py/src/dec/mod.rs +23 -0
  62. cartan-0.1.7/cartan-py/src/dec/operators.rs +450 -0
  63. cartan-0.1.7/cartan-py/src/error.rs +98 -0
  64. cartan-0.1.7/cartan-py/src/geo.rs +1037 -0
  65. cartan-0.1.7/cartan-py/src/holonomy.rs +277 -0
  66. cartan-0.1.7/cartan-py/src/lib.rs +22 -0
  67. cartan-0.1.7/cartan-py/src/manifolds/corr.rs +47 -0
  68. cartan-0.1.7/cartan-py/src/manifolds/euclidean.rs +44 -0
  69. cartan-0.1.7/cartan-py/src/manifolds/frame_field.rs +85 -0
  70. cartan-0.1.7/cartan-py/src/manifolds/grassmann.rs +509 -0
  71. cartan-0.1.7/cartan-py/src/manifolds/macros.rs +1086 -0
  72. cartan-0.1.7/cartan-py/src/manifolds/mod.rs +35 -0
  73. cartan-0.1.7/cartan-py/src/manifolds/qtensor.rs +388 -0
  74. cartan-0.1.7/cartan-py/src/manifolds/se.rs +587 -0
  75. cartan-0.1.7/cartan-py/src/manifolds/so.rs +48 -0
  76. cartan-0.1.7/cartan-py/src/manifolds/spd.rs +48 -0
  77. cartan-0.1.7/cartan-py/src/manifolds/sphere.rs +48 -0
  78. cartan-0.1.7/cartan-py/src/optim.rs +1152 -0
  79. cartan-0.1.7/cartan-py/tests/conftest.py +22 -0
  80. cartan-0.1.7/cartan-py/tests/test_batch.py +125 -0
  81. cartan-0.1.7/cartan-py/tests/test_corr.py +125 -0
  82. cartan-0.1.7/cartan-py/tests/test_dec.py +331 -0
  83. cartan-0.1.7/cartan-py/tests/test_euclidean.py +171 -0
  84. cartan-0.1.7/cartan-py/tests/test_geo.py +243 -0
  85. cartan-0.1.7/cartan-py/tests/test_grassmann.py +112 -0
  86. cartan-0.1.7/cartan-py/tests/test_holonomy.py +136 -0
  87. cartan-0.1.7/cartan-py/tests/test_optim.py +223 -0
  88. cartan-0.1.7/cartan-py/tests/test_properties.py +76 -0
  89. cartan-0.1.7/cartan-py/tests/test_qtensor.py +105 -0
  90. cartan-0.1.7/cartan-py/tests/test_se.py +227 -0
  91. cartan-0.1.7/cartan-py/tests/test_so.py +125 -0
  92. cartan-0.1.7/cartan-py/tests/test_spd.py +236 -0
  93. cartan-0.1.7/cartan-py/tests/test_sphere.py +246 -0
  94. cartan-0.1.7/pyproject.toml +25 -0
@@ -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};