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