apex_solver/manifold/mod.rs
1//! Manifold representations for optimization on non-Euclidean spaces.
2//!
3//! This module provides manifold representations commonly used in computer vision and robotics:
4//! - **SE(3)**: Special Euclidean group (rigid body transformations)
5//! - **SO(3)**: Special Orthogonal group (rotations)
6//! - **Sim(3)**: Similarity transformations
7//! - **SE(2)**: Rigid transformations in 2D
8//! - **SO(2)**: Rotations in 2D
9//!
10//! Lie group M,° | size | dim | X ∈ M | Constraint | T_E M | T_X M | Exp(T) | Comp. | Action
11//! ------------- | ------ | --- | ----------------------- | --------------- | ----------------- | --------------------- | ------------------ | ----- | ------
12//! n-D vector | Rⁿ,+ | n | n | v ∈ Rⁿ | |v-v|=0 | v ∈ Rⁿ | v ∈ Rⁿ | v = exp(v) | v₁+v₂ | v + x
13//! Circle | S¹,. | 2 | 1 | z ∈ C | z*z = 1 | iθ ∈ iR | θ ∈ R | z = exp(iθ) | z₁z₂ | zx
14//! Rotation | SO(2),.| 4 | 1 | R | RᵀR = I | [θ]x ∈ so(2) | [θ] ∈ R² | R = exp([θ]x) | R₁R₂ | Rx
15//! Rigid motion | SE(2),.| 9 | 3 | M = [R t; 0 1] | RᵀR = I | [v̂] ∈ se(2) | [v̂] ∈ R³ | Exp([v̂]) | M₁M₂ | Rx+t
16//! 3-sphere | S³,. | 4 | 3 | q ∈ H | q*q = 1 | θ/2 ∈ Hp | θ ∈ R³ | q = exp(uθ/2) | q₁q₂ | qxq*
17//! Rotation | SO(3),.| 9 | 3 | R | RᵀR = I | [θ]x ∈ so(3) | [θ] ∈ R³ | R = exp([θ]x) | R₁R₂ | Rx
18//! Rigid motion | SE(3),.| 16 | 6 | M = [R t; 0 1] | RᵀR = I | [v̂] ∈ se(3) | [v̂] ∈ R⁶ | Exp([v̂]) | M₁M₂ | Rx+t
19//!
20//! The design is inspired by the [manif](https://github.com/artivis/manif) C++ library
21//! and provides:
22//! - Analytic Jacobian computations for all operations
23//! - Right and left perturbation models
24//! - Composition and inverse operations
25//! - Exponential and logarithmic maps
26//! - Tangent space operations
27//!
28//! # Mathematical Background
29//!
30//! This module implements Lie group theory for robotics applications. Each manifold
31//! represents a Lie group with its associated tangent space (Lie algebra).
32//! Operations are differentiated with respect to perturbations on the local tangent space.
33//!
34
35use nalgebra::{Matrix3, Vector3};
36use std::ops::{Mul, Neg};
37use std::{
38 error, fmt,
39 fmt::{Display, Formatter},
40};
41
42pub mod rn;
43pub mod se2;
44pub mod se3;
45pub mod so2;
46pub mod so3;
47
48/// Errors that can occur during manifold operations.
49#[derive(Debug, Clone, PartialEq)]
50pub enum ManifoldError {
51 /// Invalid tangent vector dimension
52 InvalidTangentDimension { expected: usize, actual: usize },
53 /// Numerical instability in computation
54 NumericalInstability(String),
55 /// Invalid manifold element
56 InvalidElement(String),
57 /// Dimension validation failed during conversion
58 DimensionMismatch { expected: usize, actual: usize },
59 /// NaN or Inf detected in manifold element
60 InvalidNumber,
61 /// Normalization failed for manifold element
62 NormalizationFailed(String),
63}
64
65#[derive(Debug, Clone, PartialEq)]
66pub enum ManifoldType {
67 RN,
68 SE2,
69 SE3,
70 SO2,
71 SO3,
72}
73
74impl Display for ManifoldError {
75 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
76 match self {
77 ManifoldError::InvalidTangentDimension { expected, actual } => {
78 write!(
79 f,
80 "Invalid tangent dimension: expected {expected}, got {actual}"
81 )
82 }
83 ManifoldError::NumericalInstability(msg) => {
84 write!(f, "Numerical instability: {msg}")
85 }
86 ManifoldError::InvalidElement(msg) => {
87 write!(f, "Invalid manifold element: {msg}")
88 }
89 ManifoldError::DimensionMismatch { expected, actual } => {
90 write!(f, "Dimension mismatch: expected {expected}, got {actual}")
91 }
92 ManifoldError::InvalidNumber => {
93 write!(f, "Invalid number: NaN or Inf detected")
94 }
95 ManifoldError::NormalizationFailed(msg) => {
96 write!(f, "Normalization failed: {msg}")
97 }
98 }
99 }
100}
101
102impl error::Error for ManifoldError {}
103
104/// Result type for manifold operations.
105pub type ManifoldResult<T> = Result<T, ManifoldError>;
106
107/// Core trait for Lie group operations.
108///
109/// This trait provides the fundamental operations for Lie groups, including:
110/// - Group operations (composition, inverse, identity)
111/// - Exponential and logarithmic maps
112/// - Lie group plus/minus operations with Jacobians
113/// - Adjoint operations
114/// - Random sampling and normalization
115///
116/// The design closely follows the [manif](https://github.com/artivis/manif) C++ library.
117///
118/// # Type Parameters
119///
120/// Associated types define the mathematical structure:
121/// - `Element`: The Lie group element type (e.g., `Isometry3<f64>` for SE(3))
122/// - `TangentVector`: The tangent space vector type (e.g., `Vector6<f64>` for SE(3))
123/// - `JacobianMatrix`: The Jacobian matrix type for this Lie group
124/// - `LieAlgebra`: Associated Lie algebra type
125///
126/// # Dimensions
127///
128/// Three key dimensions characterize each Lie group:
129/// - `DIM`: Space dimension - dimension of ambient space (e.g., 3 for SE(3))
130/// - `DOF`: Degrees of freedom - tangent space dimension (e.g., 6 for SE(3))
131/// - `REP_SIZE`: Representation size - underlying data size (e.g., 7 for SE(3))
132pub trait LieGroup: Clone + PartialEq {
133 /// The tangent space vector type
134 type TangentVector: Tangent<Self>;
135
136 /// The Jacobian matrix type
137 type JacobianMatrix: Clone
138 + PartialEq
139 + Neg<Output = Self::JacobianMatrix>
140 + Mul<Output = Self::JacobianMatrix>;
141
142 /// Associated Lie algebra type
143 type LieAlgebra: Clone + PartialEq;
144
145 // Core group operations
146
147 /// Compute the inverse of this manifold element.
148 ///
149 /// For a group element g, returns g⁻¹ such that g ∘ g⁻¹ = e.
150 ///
151 /// # Arguments
152 /// * `jacobian` - Optional mutable reference to store the Jacobian ∂(g⁻¹)/∂g
153 fn inverse(&self, jacobian: Option<&mut Self::JacobianMatrix>) -> Self;
154
155 /// Compose this element with another (group multiplication).
156 ///
157 /// Computes g₁ ∘ g₂ where ∘ is the group operation.
158 ///
159 /// # Arguments
160 /// * `other` - The right operand for composition
161 /// * `jacobian_self` - Optional Jacobian ∂(g₁ ∘ g₂)/∂g₁
162 /// * `jacobian_other` - Optional Jacobian ∂(g₁ ∘ g₂)/∂g₂
163 fn compose(
164 &self,
165 other: &Self,
166 jacobian_self: Option<&mut Self::JacobianMatrix>,
167 jacobian_other: Option<&mut Self::JacobianMatrix>,
168 ) -> Self;
169
170 /// Logarithmic map from manifold to tangent space.
171 ///
172 /// Maps a group element g ∈ G to its tangent vector log(g)^∨ ∈ 𝔤.
173 ///
174 /// # Arguments
175 /// * `jacobian` - Optional Jacobian ∂log(g)^∨/∂g
176 fn log(&self, jacobian: Option<&mut Self::JacobianMatrix>) -> Self::TangentVector;
177
178 /// Vee operator: log(g)^∨.
179 ///
180 /// Maps a group element g ∈ G to its tangent vector log(g)^∨ ∈ 𝔤.
181 ///
182 /// # Arguments
183 /// * `jacobian` - Optional Jacobian ∂log(g)^∨/∂g
184 fn vee(&self) -> Self::TangentVector;
185
186 /// Act on a vector v: g ⊙ v.
187 ///
188 /// Group action on vectors (e.g., rotation for SO(3), transformation for SE(3)).
189 ///
190 /// # Arguments
191 /// * `vector` - Vector to transform
192 /// * `jacobian_self` - Optional Jacobian ∂(g ⊙ v)/∂g
193 /// * `jacobian_vector` - Optional Jacobian ∂(g ⊙ v)/∂v
194 fn act(
195 &self,
196 vector: &Vector3<f64>,
197 jacobian_self: Option<&mut Self::JacobianMatrix>,
198 jacobian_vector: Option<&mut Matrix3<f64>>,
199 ) -> Vector3<f64>;
200
201 // Adjoint operations
202
203 /// Adjoint matrix Ad(g).
204 ///
205 /// The adjoint representation maps the group to linear transformations
206 /// on the Lie algebra: Ad(g) φ = log(g ∘ exp(φ^∧) ∘ g⁻¹)^∨.
207 fn adjoint(&self) -> Self::JacobianMatrix;
208
209 // Utility operations
210
211 /// Generate a random element (useful for testing and initialization).
212 fn random() -> Self;
213
214 /// Normalize/project the element to the manifold.
215 ///
216 /// Ensures the element satisfies manifold constraints (e.g., orthogonality for rotations).
217 fn normalize(&mut self);
218
219 /// Check if the element is approximately on the manifold.
220 fn is_valid(&self, tolerance: f64) -> bool;
221
222 /// Check if the element is approximately equal to another element.
223 ///
224 /// # Arguments
225 /// * `other` - The other element to compare with
226 /// * `tolerance` - The tolerance for the comparison
227 fn is_approx(&self, other: &Self, tolerance: f64) -> bool;
228
229 // Manifold plus/minus operations
230
231 /// Right plus operation: g ⊞ φ = g ∘ exp(φ^∧).
232 ///
233 /// Applies a tangent space perturbation to this manifold element.
234 ///
235 /// # Arguments
236 /// * `tangent` - Tangent vector perturbation
237 /// * `jacobian_self` - Optional Jacobian ∂(g ⊞ φ)/∂g
238 /// * `jacobian_tangent` - Optional Jacobian ∂(g ⊞ φ)/∂φ
239 ///
240 /// # Notes
241 /// # Equation 148:
242 /// J_R⊕θ_R = R(θ)ᵀ
243 /// J_R⊕θ_θ = J_r(θ)
244 fn right_plus(
245 &self,
246 tangent: &Self::TangentVector,
247 jacobian_self: Option<&mut Self::JacobianMatrix>,
248 jacobian_tangent: Option<&mut Self::JacobianMatrix>,
249 ) -> Self {
250 let exp_tangent = tangent.exp(None);
251
252 if let Some(jac_tangent) = jacobian_tangent {
253 *jac_tangent = tangent.right_jacobian();
254 }
255
256 self.compose(&exp_tangent, jacobian_self, None)
257 }
258
259 /// Right minus operation: g₁ ⊟ g₂ = log(g₂⁻¹ ∘ g₁)^∨.
260 ///
261 /// Computes the tangent vector that transforms g₂ to g₁.
262 ///
263 /// # Arguments
264 /// * `other` - The reference element g₂
265 /// * `jacobian_self` - Optional Jacobian ∂(g₁ ⊟ g₂)/∂g₁
266 /// * `jacobian_other` - Optional Jacobian ∂(g₁ ⊟ g₂)/∂g₂
267 ///
268 /// # Notes
269 /// # Equation 149:
270 /// J_Q⊖R_Q = J_r⁻¹(θ)
271 /// J_Q⊖R_R = -J_l⁻¹(θ)
272 fn right_minus(
273 &self,
274 other: &Self,
275 jacobian_self: Option<&mut Self::JacobianMatrix>,
276 jacobian_other: Option<&mut Self::JacobianMatrix>,
277 ) -> Self::TangentVector {
278 let other_inverse = other.inverse(None);
279 let result_group = other_inverse.compose(self, None, None);
280 let result = result_group.log(None);
281
282 if let Some(jac_self) = jacobian_self {
283 *jac_self = -result.left_jacobian_inv();
284 }
285
286 if let Some(jac_other) = jacobian_other {
287 *jac_other = result.right_jacobian_inv();
288 }
289
290 result
291 }
292
293 /// Left plus operation: φ ⊞ g = exp(φ^∧) ∘ g.
294 ///
295 /// # Arguments
296 /// * `tangent` - Tangent vector perturbation
297 /// * `jacobian_tangent` - Optional Jacobian ∂(φ ⊞ g)/∂φ
298 /// * `jacobian_self` - Optional Jacobian ∂(φ ⊞ g)/∂g
299 fn left_plus(
300 &self,
301 tangent: &Self::TangentVector,
302 jacobian_tangent: Option<&mut Self::JacobianMatrix>,
303 jacobian_self: Option<&mut Self::JacobianMatrix>,
304 ) -> Self {
305 // Left plus: τ ⊕ g = exp(τ) * g
306 let exp_tangent = tangent.exp(None);
307 let result = exp_tangent.compose(self, None, None);
308
309 if let Some(jac_self) = jacobian_self {
310 // Note: jacobian_identity() is now implemented in concrete types
311 // This will be handled by the concrete implementation
312 *jac_self = self.adjoint();
313 }
314
315 if let Some(jac_tangent) = jacobian_tangent {
316 *jac_tangent = self.inverse(None).adjoint() * tangent.right_jacobian();
317 }
318
319 result
320 }
321
322 /// Left minus operation: g₁ ⊟ g₂ = log(g₁ ∘ g₂⁻¹)^∨.
323 ///
324 /// # Arguments
325 /// * `other` - The reference element g₂
326 /// * `jacobian_self` - Optional Jacobian ∂(g₁ ⊟ g₂)/∂g₁
327 /// * `jacobian_other` - Optional Jacobian ∂(g₁ ⊟ g₂)/∂g₂
328 fn left_minus(
329 &self,
330 other: &Self,
331 jacobian_self: Option<&mut Self::JacobianMatrix>,
332 jacobian_other: Option<&mut Self::JacobianMatrix>,
333 ) -> Self::TangentVector {
334 // Left minus: g1 ⊖ g2 = log(g1 * g2^{-1})
335 let other_inverse = other.inverse(None);
336 let result_group = self.compose(&other_inverse, None, None);
337 let result = result_group.log(None);
338
339 if let Some(jac_self) = jacobian_self {
340 *jac_self = result.right_jacobian_inv() * other.adjoint();
341 }
342
343 if let Some(jac_other) = jacobian_other {
344 *jac_other = -(result.right_jacobian_inv() * other.adjoint());
345 }
346
347 result
348 }
349
350 // Convenience methods (use right operations by default)
351
352 /// Convenience method for right_plus. Equivalent to g ⊞ φ.
353 fn plus(
354 &self,
355 tangent: &Self::TangentVector,
356 jacobian_self: Option<&mut Self::JacobianMatrix>,
357 jacobian_tangent: Option<&mut Self::JacobianMatrix>,
358 ) -> Self {
359 self.right_plus(tangent, jacobian_self, jacobian_tangent)
360 }
361
362 /// Convenience method for right_minus. Equivalent to g₁ ⊟ g₂.
363 fn minus(
364 &self,
365 other: &Self,
366 jacobian_self: Option<&mut Self::JacobianMatrix>,
367 jacobian_other: Option<&mut Self::JacobianMatrix>,
368 ) -> Self::TangentVector {
369 self.right_minus(other, jacobian_self, jacobian_other)
370 }
371
372 // Additional operations
373
374 /// Compute g₁⁻¹ ∘ g₂ (relative transformation).
375 ///
376 /// # Arguments
377 /// * `other` - The target element g₂
378 /// * `jacobian_self` - Optional Jacobian with respect to g₁
379 /// * `jacobian_other` - Optional Jacobian with respect to g₂
380 fn between(
381 &self,
382 other: &Self,
383 jacobian_self: Option<&mut Self::JacobianMatrix>,
384 jacobian_other: Option<&mut Self::JacobianMatrix>,
385 ) -> Self {
386 // Between: g1.between(g2) = g1^{-1} * g2
387 let self_inverse = self.inverse(None);
388 let result = self_inverse.compose(other, None, None);
389
390 if let Some(jac_self) = jacobian_self {
391 *jac_self = -result.inverse(None).adjoint();
392 }
393
394 if let Some(jac_other) = jacobian_other {
395 // Note: jacobian_identity() is now implemented in concrete types
396 // This will be handled by the concrete implementation
397 *jac_other = other.adjoint();
398 }
399
400 result
401 }
402
403 /// Get the dimension of the tangent space for this manifold element.
404 ///
405 /// For most manifolds, this returns the compile-time constant from the TangentVector type.
406 /// For dynamically-sized manifolds like Rⁿ, this method should be overridden to return
407 /// the actual runtime dimension.
408 ///
409 /// # Returns
410 /// The dimension of the tangent space (degrees of freedom)
411 ///
412 /// # Default Implementation
413 /// Returns `Self::TangentVector::DIM` which works for fixed-size manifolds
414 /// (SE2=3, SE3=6, SO2=1, SO3=3).
415 fn tangent_dim(&self) -> usize {
416 Self::TangentVector::DIM
417 }
418}
419
420/// Trait for Lie algebra operations.
421///
422/// This trait provides operations for vectors in the Lie algebra of a Lie group,
423/// including vector space operations, adjoint actions, and conversions to matrix form.
424///
425/// # Type Parameters
426///
427/// - `G`: The associated Lie group type
428pub trait Tangent<Group: LieGroup>: Clone + PartialEq {
429 // Dimension constants
430
431 /// Dimension of the tangent space
432 const DIM: usize;
433
434 // Exponential map and Jacobians
435
436 /// Exponential map to Lie group: exp(φ^∧).
437 ///
438 /// # Arguments
439 /// * `jacobian` - Optional Jacobian ∂exp(φ^∧)/∂φ
440 fn exp(&self, jacobian: Option<&mut Group::JacobianMatrix>) -> Group;
441
442 /// Right Jacobian Jr.
443 ///
444 /// Matrix Jr such that for small δφ:
445 /// exp((φ + δφ)^∧) ≈ exp(φ^∧) ∘ exp((Jr δφ)^∧)
446 fn right_jacobian(&self) -> Group::JacobianMatrix;
447
448 /// Left Jacobian Jl.
449 ///
450 /// Matrix Jl such that for small δφ:
451 /// exp((φ + δφ)^∧) ≈ exp((Jl δφ)^∧) ∘ exp(φ^∧)
452 fn left_jacobian(&self) -> Group::JacobianMatrix;
453
454 /// Inverse of right Jacobian Jr⁻¹.
455 fn right_jacobian_inv(&self) -> Group::JacobianMatrix;
456
457 /// Inverse of left Jacobian Jl⁻¹.
458 fn left_jacobian_inv(&self) -> Group::JacobianMatrix;
459
460 // Matrix representations
461
462 /// Hat operator: φ^∧ (vector to matrix).
463 ///
464 /// Maps the tangent vector to its matrix representation in the Lie algebra.
465 /// For SO(3): 3×1 vector → 3×3 skew-symmetric matrix
466 /// For SE(3): 6×1 vector → 4×4 transformation matrix
467 fn hat(&self) -> Group::LieAlgebra;
468
469 /// Small adjugate operator: adj(φ) = φ^∧.
470 ///
471 /// Maps the tangent vector to its matrix representation in the Lie algebra.
472 /// For SO(3): 3×1 vector → 3×3 skew-symmetric matrix
473 /// For SE(3): 6×1 vector → 4×4 transformation matrix
474 fn small_adj(&self) -> Group::JacobianMatrix;
475
476 /// Lie bracket: [φ, ψ] = φ ∘ ψ - ψ ∘ φ.
477 ///
478 /// Computes the Lie bracket of two tangent vectors in the Lie algebra.
479 /// For SO(3): 3×1 vector → 3×1 vector
480 /// For SE(3): 6×1 vector → 6×1 vector
481 fn lie_bracket(&self, other: &Self) -> Group::TangentVector;
482
483 /// Check if the tangent vector is approximately equal to another tangent vector.
484 ///
485 /// # Arguments
486 /// * `other` - The other tangent vector to compare with
487 /// * `tolerance` - The tolerance for the comparison
488 fn is_approx(&self, other: &Self, tolerance: f64) -> bool;
489
490 /// Get the i-th generator of the Lie algebra.
491 fn generator(&self, i: usize) -> Group::LieAlgebra;
492
493 // Utility functions
494
495 /// Zero tangent vector.
496 fn zero() -> Group::TangentVector;
497
498 /// Random tangent vector (useful for testing).
499 fn random() -> Group::TangentVector;
500
501 /// Check if the tangent vector is approximately zero.
502 fn is_zero(&self, tolerance: f64) -> bool;
503
504 /// Normalize the tangent vector to unit norm.
505 fn normalize(&mut self);
506
507 /// Return a unit tangent vector in the same direction.
508 fn normalized(&self) -> Group::TangentVector;
509}
510
511/// Trait for Lie groups that support interpolation.
512pub trait Interpolatable: LieGroup {
513 /// Linear interpolation in the manifold.
514 ///
515 /// For parameter t ∈ [0,1]: interp(g₁, g₂, 0) = g₁, interp(g₁, g₂, 1) = g₂.
516 ///
517 /// # Arguments
518 /// * `other` - Target element for interpolation
519 /// * `t` - Interpolation parameter in [0,1]
520 fn interp(&self, other: &Self, t: f64) -> Self;
521
522 /// Spherical linear interpolation (when applicable).
523 fn slerp(&self, other: &Self, t: f64) -> Self;
524}