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}