1use nalgebra::{Matrix3, Vector3};
41use std::ops::{Mul, Neg};
42use std::{
43 error, fmt,
44 fmt::{Display, Formatter},
45};
46
47pub const SMALL_ANGLE_THRESHOLD: f64 = 1e-10;
62
63pub mod rn;
64pub mod se2;
65pub mod se23;
66pub mod se3;
67pub mod sgal3;
68pub mod sim3;
69pub mod so2;
70pub mod so3;
71
72#[derive(Debug, Clone, PartialEq)]
74pub enum ManifoldError {
75 InvalidTangentDimension { expected: usize, actual: usize },
77 NumericalInstability(String),
79 InvalidElement(String),
81 DimensionMismatch { expected: usize, actual: usize },
83 InvalidNumber,
85 NormalizationFailed(String),
87}
88
89#[derive(Debug, Clone, PartialEq)]
90pub enum ManifoldType {
91 RN,
92 SE2,
93 SE3,
94 SE23,
95 SGal3,
96 Sim3,
97 SO2,
98 SO3,
99}
100
101impl Display for ManifoldError {
102 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
103 match self {
104 ManifoldError::InvalidTangentDimension { expected, actual } => {
105 write!(
106 f,
107 "Invalid tangent dimension: expected {expected}, got {actual}"
108 )
109 }
110 ManifoldError::NumericalInstability(msg) => {
111 write!(f, "Numerical instability: {msg}")
112 }
113 ManifoldError::InvalidElement(msg) => {
114 write!(f, "Invalid manifold element: {msg}")
115 }
116 ManifoldError::DimensionMismatch { expected, actual } => {
117 write!(f, "Dimension mismatch: expected {expected}, got {actual}")
118 }
119 ManifoldError::InvalidNumber => {
120 write!(f, "Invalid number: NaN or Inf detected")
121 }
122 ManifoldError::NormalizationFailed(msg) => {
123 write!(f, "Normalization failed: {msg}")
124 }
125 }
126 }
127}
128
129impl error::Error for ManifoldError {}
130
131pub type ManifoldResult<T> = Result<T, ManifoldError>;
133
134pub trait LieGroup: Clone + PartialEq {
145 type TangentVector: Tangent<Self>;
147
148 type JacobianMatrix: Clone
150 + PartialEq
151 + Neg<Output = Self::JacobianMatrix>
152 + Mul<Output = Self::JacobianMatrix>
153 + std::ops::Index<(usize, usize), Output = f64>;
154
155 type LieAlgebra: Clone + PartialEq;
157
158 fn inverse(&self, jacobian: Option<&mut Self::JacobianMatrix>) -> Self;
167
168 fn compose(
177 &self,
178 other: &Self,
179 jacobian_self: Option<&mut Self::JacobianMatrix>,
180 jacobian_other: Option<&mut Self::JacobianMatrix>,
181 ) -> Self;
182
183 fn log(&self, jacobian: Option<&mut Self::JacobianMatrix>) -> Self::TangentVector;
190
191 fn vee(&self) -> Self::TangentVector;
198
199 fn act(
208 &self,
209 vector: &Vector3<f64>,
210 jacobian_self: Option<&mut Self::JacobianMatrix>,
211 jacobian_vector: Option<&mut Matrix3<f64>>,
212 ) -> Vector3<f64>;
213
214 fn adjoint(&self) -> Self::JacobianMatrix;
221
222 fn random() -> Self;
226
227 fn jacobian_identity() -> Self::JacobianMatrix;
232
233 fn zero_jacobian() -> Self::JacobianMatrix;
238
239 fn normalize(&mut self);
243
244 fn is_valid(&self, tolerance: f64) -> bool;
246
247 fn is_approx(&self, other: &Self, tolerance: f64) -> bool;
253
254 fn right_plus(
270 &self,
271 tangent: &Self::TangentVector,
272 jacobian_self: Option<&mut Self::JacobianMatrix>,
273 jacobian_tangent: Option<&mut Self::JacobianMatrix>,
274 ) -> Self {
275 let exp_tangent = tangent.exp(None);
276
277 if let Some(jac_tangent) = jacobian_tangent {
278 *jac_tangent = tangent.right_jacobian();
279 }
280
281 self.compose(&exp_tangent, jacobian_self, None)
282 }
283
284 fn right_minus(
298 &self,
299 other: &Self,
300 jacobian_self: Option<&mut Self::JacobianMatrix>,
301 jacobian_other: Option<&mut Self::JacobianMatrix>,
302 ) -> Self::TangentVector {
303 let other_inverse = other.inverse(None);
304 let result_group = other_inverse.compose(self, None, None);
305 let result = result_group.log(None);
306
307 if let Some(jac_self) = jacobian_self {
308 *jac_self = result.right_jacobian_inv();
309 }
310
311 if let Some(jac_other) = jacobian_other {
312 *jac_other = -result.left_jacobian_inv();
313 }
314
315 result
316 }
317
318 fn left_plus(
325 &self,
326 tangent: &Self::TangentVector,
327 jacobian_tangent: Option<&mut Self::JacobianMatrix>,
328 jacobian_self: Option<&mut Self::JacobianMatrix>,
329 ) -> Self {
330 let exp_tangent = tangent.exp(None);
331 let result = exp_tangent.compose(self, None, None);
332
333 if let Some(jac_self) = jacobian_self {
334 *jac_self = self.adjoint();
335 }
336
337 if let Some(jac_tangent) = jacobian_tangent {
338 *jac_tangent = self.inverse(None).adjoint() * tangent.right_jacobian();
339 }
340
341 result
342 }
343
344 fn left_minus(
351 &self,
352 other: &Self,
353 jacobian_self: Option<&mut Self::JacobianMatrix>,
354 jacobian_other: Option<&mut Self::JacobianMatrix>,
355 ) -> Self::TangentVector {
356 let other_inverse = other.inverse(None);
357 let result_group = self.compose(&other_inverse, None, None);
358 let result = result_group.log(None);
359
360 if let Some(jac_self) = jacobian_self {
361 *jac_self = result.right_jacobian_inv() * other.adjoint();
362 }
363
364 if let Some(jac_other) = jacobian_other {
365 *jac_other = -(result.right_jacobian_inv() * other.adjoint());
366 }
367
368 result
369 }
370
371 fn plus(
375 &self,
376 tangent: &Self::TangentVector,
377 jacobian_self: Option<&mut Self::JacobianMatrix>,
378 jacobian_tangent: Option<&mut Self::JacobianMatrix>,
379 ) -> Self {
380 self.right_plus(tangent, jacobian_self, jacobian_tangent)
381 }
382
383 fn minus(
385 &self,
386 other: &Self,
387 jacobian_self: Option<&mut Self::JacobianMatrix>,
388 jacobian_other: Option<&mut Self::JacobianMatrix>,
389 ) -> Self::TangentVector {
390 self.right_minus(other, jacobian_self, jacobian_other)
391 }
392
393 fn between(
402 &self,
403 other: &Self,
404 jacobian_self: Option<&mut Self::JacobianMatrix>,
405 jacobian_other: Option<&mut Self::JacobianMatrix>,
406 ) -> Self {
407 let self_inverse = self.inverse(None);
408 let result = self_inverse.compose(other, None, None);
409
410 if let Some(jac_self) = jacobian_self {
411 *jac_self = -result.inverse(None).adjoint();
412 }
413
414 if let Some(jac_other) = jacobian_other {
415 *jac_other = Self::jacobian_identity();
416 }
417
418 result
419 }
420
421 fn tangent_dim(&self) -> usize {
434 Self::TangentVector::DIM
435 }
436}
437
438pub trait Tangent<Group: LieGroup>: Clone + PartialEq {
447 const DIM: usize;
456
457 fn is_dynamic() -> bool {
462 Self::DIM == 0
463 }
464
465 fn exp(&self, jacobian: Option<&mut Group::JacobianMatrix>) -> Group;
472
473 fn right_jacobian(&self) -> Group::JacobianMatrix;
478
479 fn left_jacobian(&self) -> Group::JacobianMatrix;
484
485 fn right_jacobian_inv(&self) -> Group::JacobianMatrix;
487
488 fn left_jacobian_inv(&self) -> Group::JacobianMatrix;
490
491 fn hat(&self) -> Group::LieAlgebra;
499
500 fn small_adj(&self) -> Group::JacobianMatrix;
506
507 fn lie_bracket(&self, other: &Self) -> Group::TangentVector;
513
514 fn is_approx(&self, other: &Self, tolerance: f64) -> bool;
520
521 fn generator(&self, i: usize) -> Group::LieAlgebra;
523
524 fn zero() -> Group::TangentVector;
528
529 fn random() -> Group::TangentVector;
531
532 fn is_zero(&self, tolerance: f64) -> bool;
534
535 fn normalize(&mut self);
537
538 fn normalized(&self) -> Group::TangentVector;
540}
541
542pub trait Interpolatable: LieGroup {
544 fn interp(&self, other: &Self, t: f64) -> Self;
552
553 fn slerp(&self, other: &Self, t: f64) -> Self;
555}
556
557#[cfg(test)]
558mod tests {
559 use crate::LieGroup;
560 use crate::Tangent;
561 use crate::so2::{SO2, SO2Tangent};
562 use crate::so3::{SO3, SO3Tangent};
563 use crate::{ManifoldError, ManifoldType};
564 use nalgebra::Matrix1;
565
566 fn make_so2(angle: f64) -> SO2 {
567 SO2::from_angle(angle)
568 }
569
570 fn make_so2_tangent(angle: f64) -> SO2Tangent {
571 SO2Tangent::new(angle)
572 }
573
574 #[test]
575 fn manifold_error_display_invalid_tangent_dimension() {
576 let e = ManifoldError::InvalidTangentDimension {
577 expected: 3,
578 actual: 6,
579 };
580 let s = e.to_string();
581 assert!(s.contains("3"), "got: {s}");
582 assert!(s.contains("6"), "got: {s}");
583 }
584
585 #[test]
586 fn manifold_error_display_numerical_instability() {
587 let e = ManifoldError::NumericalInstability("singularity".to_string());
588 assert!(e.to_string().contains("singularity"));
589 }
590
591 #[test]
592 fn manifold_error_display_invalid_element() {
593 let e = ManifoldError::InvalidElement("bad quaternion".to_string());
594 assert!(e.to_string().contains("bad quaternion"));
595 }
596
597 #[test]
598 fn manifold_error_display_dimension_mismatch() {
599 let e = ManifoldError::DimensionMismatch {
600 expected: 4,
601 actual: 3,
602 };
603 let s = e.to_string();
604 assert!(s.contains("4") && s.contains("3"), "got: {s}");
605 }
606
607 #[test]
608 fn manifold_error_display_invalid_number() {
609 let e = ManifoldError::InvalidNumber;
610 assert!(!e.to_string().is_empty());
611 }
612
613 #[test]
614 fn manifold_error_display_normalization_failed() {
615 let e = ManifoldError::NormalizationFailed("zero vector".to_string());
616 assert!(e.to_string().contains("zero vector"));
617 }
618
619 #[test]
620 fn manifold_error_is_std_error() {
621 let e = ManifoldError::InvalidNumber;
622 let _: &dyn std::error::Error = &e;
623 }
624
625 #[test]
626 fn manifold_type_variants_are_distinct() {
627 let types = [
628 ManifoldType::RN,
629 ManifoldType::SE2,
630 ManifoldType::SE3,
631 ManifoldType::SE23,
632 ManifoldType::SGal3,
633 ManifoldType::Sim3,
634 ManifoldType::SO2,
635 ManifoldType::SO3,
636 ];
637 assert_eq!(types.len(), 8);
638 assert_eq!(ManifoldType::SO3, ManifoldType::SO3);
639 assert_ne!(ManifoldType::SO2, ManifoldType::SO3);
640 }
641
642 #[test]
643 fn default_right_plus_no_jacobians() {
644 let g = make_so2(0.3);
645 let t = make_so2_tangent(0.1);
646 let result = g.right_plus(&t, None, None);
647 assert!(result.is_valid(1e-9));
648 }
649
650 #[test]
651 fn default_right_plus_with_jacobians() {
652 let g = make_so2(0.3);
653 let t = make_so2_tangent(0.1);
654 let mut j_self = Matrix1::zeros();
655 let mut j_tan = Matrix1::zeros();
656 let result = g.right_plus(&t, Some(&mut j_self), Some(&mut j_tan));
657 assert!(result.is_valid(1e-9));
658 assert!(j_tan[0].is_finite());
659 }
660
661 #[test]
662 fn default_right_minus_no_jacobians() {
663 let g1 = make_so2(0.5);
664 let g2 = make_so2(0.2);
665 let _t = g1.right_minus(&g2, None, None);
666 }
667
668 #[test]
669 fn default_right_minus_with_jacobians() {
670 let g1 = make_so2(0.5);
671 let g2 = make_so2(0.2);
672 let mut j_self = Matrix1::zeros();
673 let mut j_other = Matrix1::zeros();
674 let _t = g1.right_minus(&g2, Some(&mut j_self), Some(&mut j_other));
675 assert!(j_self[0].is_finite());
676 assert!(j_other[0].is_finite());
677 }
678
679 #[test]
680 fn default_left_plus_no_jacobians() {
681 let g = make_so2(0.3);
682 let t = make_so2_tangent(0.1);
683 let result = g.left_plus(&t, None, None);
684 assert!(result.is_valid(1e-9));
685 }
686
687 #[test]
688 fn default_left_plus_with_jacobians() {
689 let g = make_so2(0.3);
690 let t = make_so2_tangent(0.1);
691 let mut j_tan = Matrix1::zeros();
692 let mut j_self = Matrix1::zeros();
693 let result = g.left_plus(&t, Some(&mut j_tan), Some(&mut j_self));
694 assert!(result.is_valid(1e-9));
695 assert!(j_tan[0].is_finite());
696 assert!(j_self[0].is_finite());
697 }
698
699 #[test]
700 fn default_left_minus_no_jacobians() {
701 let g1 = make_so2(0.5);
702 let g2 = make_so2(0.2);
703 let _t = g1.left_minus(&g2, None, None);
704 }
705
706 #[test]
707 fn default_left_minus_with_jacobians() {
708 let g1 = make_so2(0.5);
709 let g2 = make_so2(0.2);
710 let mut j_self = Matrix1::zeros();
711 let mut j_other = Matrix1::zeros();
712 let _t = g1.left_minus(&g2, Some(&mut j_self), Some(&mut j_other));
713 assert!(j_self[0].is_finite());
714 assert!(j_other[0].is_finite());
715 }
716
717 #[test]
718 fn default_plus_delegates_to_right_plus() {
719 let g = make_so2(0.3);
720 let t = make_so2_tangent(0.1);
721 let r1 = g.plus(&t, None, None);
722 let r2 = g.right_plus(&t, None, None);
723 assert!(r1.is_approx(&r2, 1e-9));
724 }
725
726 #[test]
727 fn default_minus_delegates_to_right_minus() {
728 let g1 = make_so2(0.5);
729 let g2 = make_so2(0.2);
730 let t1 = g1.minus(&g2, None, None);
731 let t2 = g1.right_minus(&g2, None, None);
732 assert!(t1.is_approx(&t2, 1e-9));
733 }
734
735 #[test]
736 fn default_between_no_jacobians() {
737 let g1 = make_so2(0.3);
738 let g2 = make_so2(0.7);
739 let b = g1.between(&g2, None, None);
740 assert!(b.is_valid(1e-9));
741 }
742
743 #[test]
744 fn default_between_with_jacobians() {
745 let g1 = make_so2(0.3);
746 let g2 = make_so2(0.7);
747 let mut j_self = Matrix1::zeros();
748 let mut j_other = Matrix1::zeros();
749 let b = g1.between(&g2, Some(&mut j_self), Some(&mut j_other));
750 assert!(b.is_valid(1e-9));
751 assert!(j_self[0].is_finite());
752 assert!(j_other[0].is_finite());
753 }
754
755 #[test]
756 fn default_tangent_dim_returns_dof() {
757 let g = make_so2(0.0);
758 assert_eq!(g.tangent_dim(), 1); }
760
761 #[test]
762 fn tangent_is_dynamic_false_for_so2() {
763 assert!(!SO2Tangent::is_dynamic());
764 }
765
766 #[test]
767 fn manifold_error_clone_and_partial_eq() {
768 let e = ManifoldError::InvalidTangentDimension {
769 expected: 1,
770 actual: 2,
771 };
772 let e2 = e.clone();
773 assert_eq!(e, e2);
774
775 let e3 = ManifoldError::NumericalInstability("x".to_string());
776 let e4 = e3.clone();
777 assert_eq!(e3, e4);
778
779 let e5 = ManifoldError::InvalidElement("y".to_string());
780 let e6 = e5.clone();
781 assert_eq!(e5, e6);
782
783 let e7 = ManifoldError::DimensionMismatch {
784 expected: 3,
785 actual: 4,
786 };
787 let e8 = e7.clone();
788 assert_eq!(e7, e8);
789
790 let e9 = ManifoldError::InvalidNumber;
791 let e10 = e9.clone();
792 assert_eq!(e9, e10);
793
794 let e11 = ManifoldError::NormalizationFailed("z".to_string());
795 let e12 = e11.clone();
796 assert_eq!(e11, e12);
797 }
798
799 #[test]
800 fn manifold_type_clone_and_eq() {
801 let all_types = [
802 ManifoldType::RN,
803 ManifoldType::SE2,
804 ManifoldType::SE3,
805 ManifoldType::SE23,
806 ManifoldType::SGal3,
807 ManifoldType::Sim3,
808 ManifoldType::SO2,
809 ManifoldType::SO3,
810 ];
811 for t in &all_types {
812 let t2 = t.clone();
813 assert_eq!(t, &t2);
814 }
815 assert_ne!(ManifoldType::RN, ManifoldType::SE2);
817 assert_ne!(ManifoldType::SE2, ManifoldType::SE3);
818 assert_ne!(ManifoldType::SE3, ManifoldType::SE23);
819 assert_ne!(ManifoldType::SE23, ManifoldType::SGal3);
820 assert_ne!(ManifoldType::SGal3, ManifoldType::Sim3);
821 assert_ne!(ManifoldType::Sim3, ManifoldType::SO2);
822 assert_ne!(ManifoldType::SO2, ManifoldType::SO3);
823 }
824
825 #[test]
826 fn manifold_type_debug() {
827 let s = format!("{:?}", ManifoldType::RN);
828 assert!(!s.is_empty());
829 let s2 = format!("{:?}", ManifoldType::SE23);
830 assert!(!s2.is_empty());
831 let s3 = format!("{:?}", ManifoldType::SGal3);
832 assert!(!s3.is_empty());
833 let s4 = format!("{:?}", ManifoldType::Sim3);
834 assert!(!s4.is_empty());
835 }
836
837 #[test]
838 fn manifold_error_debug() {
839 let e = ManifoldError::InvalidNumber;
840 let s = format!("{e:?}");
841 assert!(!s.is_empty());
842 }
843
844 #[test]
847 fn so3_default_right_plus_with_jacobians() {
848 use crate::LieGroup;
849 let r = SO3::from_euler_angles(0.1, 0.2, 0.3);
850 let t = SO3Tangent::new(nalgebra::Vector3::new(0.05, 0.0, 0.0));
851 let mut j_self = nalgebra::Matrix3::zeros();
852 let mut j_tan = nalgebra::Matrix3::zeros();
853 let result = r.right_plus(&t, Some(&mut j_self), Some(&mut j_tan));
854 assert!(result.is_valid(1e-6));
855 assert!(j_self[(0, 0)].is_finite());
856 assert!(j_tan[(0, 0)].is_finite());
857 }
858
859 #[test]
860 fn so3_default_left_plus_with_jacobians() {
861 use crate::LieGroup;
862 let r = SO3::from_euler_angles(0.1, 0.2, 0.3);
863 let t = SO3Tangent::new(nalgebra::Vector3::new(0.05, 0.0, 0.0));
864 let mut j_tan = nalgebra::Matrix3::zeros();
865 let mut j_self = nalgebra::Matrix3::zeros();
866 let result = r.left_plus(&t, Some(&mut j_tan), Some(&mut j_self));
867 assert!(result.is_valid(1e-6));
868 assert!(j_tan[(0, 0)].is_finite());
869 assert!(j_self[(0, 0)].is_finite());
870 }
871
872 #[test]
873 fn so3_default_right_minus_with_jacobians() {
874 use crate::LieGroup;
875 let r1 = SO3::from_euler_angles(0.3, 0.1, 0.2);
876 let r2 = SO3::from_euler_angles(0.1, 0.0, 0.1);
877 let mut j_self = nalgebra::Matrix3::zeros();
878 let mut j_other = nalgebra::Matrix3::zeros();
879 let _t = r1.right_minus(&r2, Some(&mut j_self), Some(&mut j_other));
880 assert!(j_self[(0, 0)].is_finite());
881 assert!(j_other[(0, 0)].is_finite());
882 }
883
884 #[test]
885 fn so3_default_left_minus_with_jacobians() {
886 use crate::LieGroup;
887 let r1 = SO3::from_euler_angles(0.3, 0.1, 0.2);
888 let r2 = SO3::from_euler_angles(0.1, 0.0, 0.1);
889 let mut j_self = nalgebra::Matrix3::zeros();
890 let mut j_other = nalgebra::Matrix3::zeros();
891 let _t = r1.left_minus(&r2, Some(&mut j_self), Some(&mut j_other));
892 assert!(j_self[(0, 0)].is_finite());
893 assert!(j_other[(0, 0)].is_finite());
894 }
895
896 #[test]
897 fn so3_default_between_with_jacobians() {
898 use crate::LieGroup;
899 let r1 = SO3::from_euler_angles(0.1, 0.2, 0.3);
900 let r2 = SO3::from_euler_angles(0.4, 0.1, 0.2);
901 let mut j_self = nalgebra::Matrix3::zeros();
902 let mut j_other = nalgebra::Matrix3::zeros();
903 let b = r1.between(&r2, Some(&mut j_self), Some(&mut j_other));
904 assert!(b.is_valid(1e-6));
905 assert!(j_self[(0, 0)].is_finite());
906 assert!(j_other[(0, 0)].is_finite());
907 }
908
909 #[test]
910 fn so3_default_tangent_dim() {
911 use crate::LieGroup;
912 let r = SO3::identity();
913 assert_eq!(r.tangent_dim(), 3);
914 }
915}