Skip to main content

lie_groups/
traits.rs

1//! Traits for Lie groups and Lie algebras.
2//!
3//! This module defines the core abstractions for Lie theory. These traits
4//! capture the mathematical structure
5//! of Lie groups as both algebraic objects (groups) and geometric objects
6//! (differentiable manifolds).
7//!
8//! # Mathematical Background
9//!
10//! A **Lie group** G is a smooth manifold that is also a group, where the
11//! group operations are smooth maps:
12//!
13//! - Multiplication: G × G → G is smooth
14//! - Inversion: G → G is smooth
15//! - Identity: e ∈ G
16//!
17//! A **Lie algebra** 𝔤 is the tangent space at the identity, with a Lie bracket
18//! operation [·,·] : 𝔤 × 𝔤 → 𝔤 satisfying bilinearity, antisymmetry, and the Jacobi identity.
19//!
20//! The **exponential map** exp: 𝔤 → G connects the Lie algebra to the Lie group,
21//! mapping tangent vectors to group elements via one-parameter subgroups.
22//!
23//! # Design Philosophy
24//!
25//! These traits enable **generic programming over Lie groups**: algorithms written
26//! against the `LieGroup` trait work for SU(2), U(1), SU(3), etc. without modification.
27//! This mirrors how mathematicians work—proving theorems for "a Lie group G"
28//! rather than for each specific group.
29//!
30//! # Examples
31//!
32//! ```
33//! use lie_groups::{LieGroup, SU2};
34//!
35//! // Generic parallel transport along a path
36//! fn parallel_transport<G: LieGroup>(path: &[G]) -> G {
37//!     path.iter().fold(G::identity(), |acc, g| acc.compose(g))
38//! }
39//!
40//! // Works for any Lie group!
41//! let su2_path = vec![SU2::rotation_x(0.1), SU2::rotation_y(0.2)];
42//! let holonomy = parallel_transport(&su2_path);
43//! ```
44//!
45//! # Constrained Representations: Compile-Time Guarantees
46//!
47//! This module uses **constrained representations** to encode mathematical
48//! properties at the type level. The key insight: instead of checking if a
49//! value satisfies a property at runtime, use a representation that makes
50//! invalid values **unrepresentable**.
51//!
52//! ## The Pattern
53//!
54//! | Property | Representation | Why It Works |
55//! |----------|----------------|--------------|
56//! | Traceless | Basis coefficients | Pauli/Gell-Mann matrices are traceless |
57//! | Anti-Hermitian | `i·(Hermitian basis)` | `(iH)† = -iH† = -iH` |
58//! | Unit norm | Private fields + normalizing constructor | Can only create unit values |
59//! | Fixed size | `[T; N]` instead of `Vec<T>` | Compile-time size guarantee |
60//!
61//! ## Example: Traceless by Construction
62//!
63//! ```text
64//! // BAD: General matrix, might not be traceless
65//! struct BadAlgebra { matrix: [[f64; 2]; 2] }  // tr(M) could be anything
66//!
67//! // GOOD: Pauli basis coefficients, always traceless
68//! struct Su2Algebra([f64; 3]);  // X = a·iσ₁ + b·iσ₂ + c·iσ₃, tr(X) = 0 always
69//! ```
70//!
71//! ## Connection to Lean Proofs
72//!
73//! The marker traits in this module connect to formal proofs:
74//!
75//! - [`TracelessByConstruction`]: Combined with the Lean theorem
76//!   `det_exp_eq_exp_trace`, gives `det(exp(X)) = exp(0) = 1` for all X.
77//!
78//! - [`AntiHermitianByConstruction`]: Combined with `exp_antiHermitian_unitary`,
79//!   gives `exp(X)† · exp(X) = I` for all X.
80//!
81//! These are **compile-time guarantees**: if your code compiles with the trait
82//! bound `A: TracelessByConstruction`, you know all algebra elements are
83//! traceless without any runtime checks.
84//!
85//! ## Zero Runtime Overhead
86//!
87//! Marker traits are empty—no methods, no data:
88//!
89//! ```ignore
90//! pub trait TracelessByConstruction: LieAlgebra {}
91//! impl TracelessByConstruction for Su2Algebra {}  // Empty impl
92//! ```
93//!
94//! The compiler uses them for type checking, then erases them completely.
95//! Same pattern as `Send`, `Sync`, `Copy` in std.
96//!
97//! ## When to Use Each Pattern
98//!
99//! | Pattern | Use When | Example |
100//! |---------|----------|---------|
101//! | Sealed marker trait | Property depends on type, not value | `TracelessByConstruction` |
102//! | Private fields | Property depends on value (invariant) | `UnitQuaternion` unit norm |
103//! | Fixed-size array | Size is known at compile time | `Plaquette([NodeIndex; 4])` |
104//! | Builder pattern | Invariant built incrementally | Path with consecutive edges |
105
106use num_complex::Complex;
107
108// ============================================================================
109// Sealed Trait Pattern for Marker Traits
110// ============================================================================
111//
112// Marker traits (Compact, Abelian, Simple, SemiSimple) are sealed to prevent
113// incorrect claims about mathematical properties. These encode theorems:
114// - "U(1) is abelian" is a mathematical fact that shouldn't be claimable by arbitrary types
115// - "SU(2) is compact" likewise
116//
117// The core `LieGroup` and `LieAlgebra` traits are OPEN - the associated type
118// constraints provide compile-time safety without sealing.
119
120mod sealed {
121    // Import types for cleaner impl blocks
122    use super::super::rplus::RPlus;
123    use super::super::so3::{So3Algebra, SO3};
124    use super::super::su2::{Su2Algebra, SU2};
125    use super::super::su3::{Su3Algebra, SU3};
126    use super::super::sun::{SunAlgebra, SUN};
127    use super::super::u1::{U1Algebra, U1};
128
129    /// Sealed trait for compact groups.
130    pub trait SealedCompact {}
131
132    /// Sealed trait for abelian groups.
133    pub trait SealedAbelian {}
134
135    /// Sealed trait for simple groups.
136    pub trait SealedSimple {}
137
138    /// Sealed trait for semi-simple groups.
139    pub trait SealedSemiSimple {}
140
141    /// Sealed trait for algebras that are traceless by construction.
142    ///
143    /// An algebra is "traceless by construction" if its representation
144    /// structurally guarantees tr(X) = 0 for all elements X.
145    ///
146    /// # Examples
147    /// - `Su2Algebra([f64; 3])`: Pauli basis coefficients → always traceless
148    /// - `Su3Algebra([f64; 8])`: Gell-Mann basis coefficients → always traceless
149    ///
150    /// # Non-examples
151    /// - `U1Algebra(f64)`: Represents iℝ, trace = i·a ≠ 0
152    /// - General matrix algebras: Can have arbitrary trace
153    pub trait SealedTraceless {}
154
155    /// Sealed trait for algebras that are anti-Hermitian by construction.
156    ///
157    /// An algebra is "anti-Hermitian by construction" if its representation
158    /// structurally guarantees X† = -X for all elements X.
159    ///
160    /// # Examples
161    /// - `Su2Algebra`: Uses i·σ basis (anti-Hermitian)
162    /// - `U1Algebra`: Represents iℝ ⊂ ℂ (purely imaginary = anti-Hermitian)
163    /// - `So3Algebra`: Real antisymmetric matrices (= anti-Hermitian)
164    pub trait SealedAntiHermitian {}
165
166    // =========================================================================
167    // Sealed trait implementations
168    // =========================================================================
169
170    // Compact groups (all our groups are compact)
171    impl SealedCompact for SU2 {}
172    impl SealedCompact for SU3 {}
173    impl SealedCompact for U1 {}
174    impl SealedCompact for SO3 {}
175    impl<const N: usize> SealedCompact for SUN<N> {}
176
177    // Abelian groups
178    impl SealedAbelian for U1 {}
179    impl SealedAbelian for RPlus {}
180
181    // Simple groups (SU(2), SU(3), SO(3), SU(N) for N >= 2)
182    impl SealedSimple for SU2 {}
183    impl SealedSimple for SU3 {}
184    impl SealedSimple for SO3 {}
185    // SUN<N> is simple (hence semi-simple) for all N >= 2.
186    // The const assert in SUN prevents N < 2 at compile time.
187    impl<const N: usize> SealedSimple for SUN<N> {}
188
189    // Semi-simple groups (all simple groups are semi-simple)
190    impl SealedSemiSimple for SU2 {}
191    impl SealedSemiSimple for SU3 {}
192    impl SealedSemiSimple for SO3 {}
193    impl<const N: usize> SealedSemiSimple for SUN<N> {}
194
195    // Traceless algebras (representation guarantees tr(X) = 0)
196    // NOT U1Algebra: u(1) = iℝ has tr(ia) = ia ≠ 0
197    impl SealedTraceless for Su2Algebra {}
198    impl SealedTraceless for Su3Algebra {}
199    impl SealedTraceless for So3Algebra {}
200    impl<const N: usize> SealedTraceless for SunAlgebra<N> {}
201
202    // Anti-Hermitian algebras (representation guarantees X† = -X)
203    impl SealedAntiHermitian for Su2Algebra {}
204    impl SealedAntiHermitian for Su3Algebra {}
205    impl SealedAntiHermitian for So3Algebra {}
206    impl SealedAntiHermitian for U1Algebra {}
207    impl<const N: usize> SealedAntiHermitian for SunAlgebra<N> {}
208}
209
210// ============================================================================
211// Marker Traits for Mathematical Properties
212// ============================================================================
213
214/// Marker trait for compact Lie groups.
215///
216/// A Lie group is **compact** if it is compact as a topological space.
217///
218/// # Examples
219/// - `U(1)`, `SU(2)`, `SU(3)`, `SO(3)` - all compact
220///
221/// # Significance
222/// - Representations are completely reducible
223/// - Yang-Mills theory well-defined
224/// - Haar measure exists
225///
226/// # Sealed Trait
227///
228/// This trait is sealed - only verified compact groups can implement it.
229pub trait Compact: LieGroup + sealed::SealedCompact {}
230
231/// Marker trait for abelian (commutative) Lie groups.
232///
233/// Satisfies: `g.compose(&h) == h.compose(&g)` for all g, h
234///
235/// # Examples
236/// - `U(1)` - abelian (electromagnetism)
237///
238/// # Type Safety
239/// ```ignore
240/// fn maxwell_theory<G: Abelian>(conn: &NetworkConnection<G>) {
241///     // Compiles only for abelian groups!
242/// }
243/// ```
244///
245/// # Sealed Trait
246///
247/// This trait is sealed - only verified abelian groups can implement it.
248pub trait Abelian: LieGroup + sealed::SealedAbelian {}
249
250/// Marker trait for simple Lie groups (no non-trivial normal subgroups).
251///
252/// # Examples
253/// - `SU(2)`, `SU(3)`, `SO(3)` - simple
254///
255/// # Sealed Trait
256///
257/// This trait is sealed - only verified simple groups can implement it.
258pub trait Simple: SemiSimple + sealed::SealedSimple {}
259
260/// Marker trait for semi-simple Lie groups (products of simple groups).
261///
262/// # Examples
263/// - `SU(2)`, `SU(3)` - semi-simple
264///
265/// # Sealed Trait
266///
267/// This trait is sealed - only verified semi-simple groups can implement it.
268pub trait SemiSimple: LieGroup + sealed::SealedSemiSimple {}
269
270/// Marker trait for Lie algebras whose representation is structurally traceless.
271///
272/// A Lie algebra is "traceless by construction" when its representation
273/// **guarantees** tr(X) = 0 for all elements X, without runtime verification.
274///
275/// # Mathematical Significance
276///
277/// Combined with the theorem `det(exp(X)) = exp(tr(X))` (proven in Lean as
278/// `Matrix.det_exp_eq_exp_trace`), this gives a **compile-time guarantee**:
279///
280/// ```text
281/// A: TracelessByConstruction  ⟹  tr(X) = 0 for all X ∈ A
282///                             ⟹  det(exp(X)) = exp(0) = 1
283/// ```
284///
285/// Therefore, `exp` maps traceless algebras to special (det = 1) groups.
286///
287/// # Examples
288///
289/// - `Su2Algebra([f64; 3])`: Pauli basis coefficients → always traceless
290/// - `Su3Algebra([f64; 8])`: Gell-Mann basis coefficients → always traceless
291/// - `So3Algebra([f64; 3])`: Antisymmetric matrices → always traceless
292///
293/// # Non-examples
294///
295/// - `U1Algebra(f64)`: Represents iℝ ⊂ ℂ, where tr(ia) = ia ≠ 0
296/// - General `Matrix<N,N>`: Can have arbitrary trace
297///
298/// # Type-Level Proof Pattern
299///
300/// ```ignore
301/// fn exp_to_special_unitary<A>(x: &A) -> SpecialUnitary
302/// where
303///     A: TracelessByConstruction,
304/// {
305///     // Compiler knows: tr(x) = 0 by construction
306///     // Lean theorem: det(exp(x)) = exp(tr(x)) = exp(0) = 1
307///     // Therefore: exp(x) ∈ SU(n), not just U(n)
308/// }
309/// ```
310///
311/// # Sealed Trait
312///
313/// This trait is sealed - only algebras with verified traceless representations
314/// can implement it.
315pub trait TracelessByConstruction: LieAlgebra + sealed::SealedTraceless {}
316
317/// Marker trait for Lie algebras whose representation is structurally anti-Hermitian.
318///
319/// A Lie algebra is "anti-Hermitian by construction" when its representation
320/// **guarantees** X† = -X for all elements X, without runtime verification.
321///
322/// # Mathematical Significance
323///
324/// Combined with the theorem `exp(X)† · exp(X) = I` for anti-Hermitian X
325/// (proven in Lean as `Matrix.exp_antiHermitian_unitary`), this gives a
326/// **compile-time guarantee**:
327///
328/// ```text
329/// A: AntiHermitianByConstruction  ⟹  X† = -X for all X ∈ A
330///                                 ⟹  exp(X) is unitary
331/// ```
332///
333/// # Examples
334///
335/// - `Su2Algebra`: Uses i·σ basis (anti-Hermitian Pauli matrices)
336/// - `Su3Algebra`: Uses i·λ basis (anti-Hermitian Gell-Mann matrices)
337/// - `So3Algebra`: Real antisymmetric matrices (= anti-Hermitian over ℝ)
338/// - `U1Algebra`: Represents iℝ ⊂ ℂ (purely imaginary = anti-Hermitian)
339///
340/// # Sealed Trait
341///
342/// This trait is sealed - only algebras with verified anti-Hermitian representations
343/// can implement it.
344pub trait AntiHermitianByConstruction: LieAlgebra + sealed::SealedAntiHermitian {}
345
346/// Trait for Lie algebra elements.
347///
348/// A Lie algebra 𝔤 is a vector space equipped with a bilinear, antisymmetric
349/// operation called the Lie bracket [·,·] : 𝔤 × 𝔤 → 𝔤 that satisfies the Jacobi identity.
350///
351/// In this library, we represent Lie algebras as the tangent space at the identity
352/// of a Lie group. For matrix groups, algebra elements are traceless anti-Hermitian matrices.
353///
354/// # Mathematical Structure
355///
356/// For classical Lie groups:
357/// - **u(1)**: ℝ (1-dimensional)
358/// - **su(2)**: ℝ³ (3-dimensional, isomorphic to pure quaternions)
359/// - **su(3)**: ℝ⁸ (8-dimensional, Gell-Mann basis)
360///
361/// # Design
362///
363/// This trait enables generic gradient descent on Lie groups via the exponential map.
364/// The gradient lives in the Lie algebra, and we use `exp: 𝔤 → G` to move on the manifold.
365///
366/// # Examples
367///
368/// ```ignore
369/// use lie_groups::{LieAlgebra, Su2Algebra};
370///
371/// let v = Su2Algebra::basis_element(0);  // Pauli X direction
372/// let w = Su2Algebra::basis_element(1);  // Pauli Y direction
373/// let sum = v.add(&w);                   // Linear combination
374/// let scaled = v.scale(2.0);             // Scalar multiplication
375/// ```
376///
377/// # Open Trait
378///
379/// This trait is open for implementation. The associated type constraints
380/// on `LieGroup` (requiring `type Algebra: LieAlgebra`) provide compile-time
381/// safety by enforcing group-algebra correspondence.
382pub trait LieAlgebra: Clone + Sized {
383    /// Dimension of the Lie algebra as a compile-time constant.
384    ///
385    /// This enables compile-time dimension checking where possible.
386    /// For example, `from_components` can verify array length at compile time
387    /// when called with a fixed-size array.
388    ///
389    /// # Values
390    ///
391    /// - `U1Algebra`: 1
392    /// - `Su2Algebra`: 3
393    /// - `So3Algebra`: 3
394    /// - `Su3Algebra`: 8
395    /// - `SunAlgebra<N>`: N² - 1
396    ///
397    /// # Note
398    ///
399    /// Rust doesn't yet support `where A: LieAlgebra<DIM = 3>` bounds
400    /// (requires `generic_const_exprs`). Use `const_assert!` or runtime
401    /// checks with `Self::DIM` for now.
402    const DIM: usize;
403
404    /// Dimension of the Lie algebra (dim 𝔤).
405    ///
406    /// This is the dimension as a real vector space:
407    /// - u(1): 1
408    /// - su(2): 3
409    /// - su(3): 8
410    /// - so(3): 3
411    ///
412    /// # Note
413    ///
414    /// Prefer using [`Self::DIM`] for compile-time checks. This method
415    /// exists for backward compatibility and dynamic dispatch scenarios.
416    #[must_use]
417    fn dim() -> usize {
418        Self::DIM
419    }
420
421    /// Zero element (additive identity) 0 ∈ 𝔤.
422    ///
423    /// Satisfies: `v.add(&Self::zero()) == v` for all v ∈ 𝔤.
424    #[must_use]
425    fn zero() -> Self;
426
427    /// Add two algebra elements: v + w
428    ///
429    /// The Lie algebra is a vector space, so addition is defined.
430    ///
431    /// # Properties
432    ///
433    /// - Commutative: `v.add(&w) == w.add(&v)`
434    /// - Associative: `u.add(&v.add(&w)) == u.add(&v).add(&w)`
435    /// - Identity: `v.add(&Self::zero()) == v`
436    #[must_use]
437    fn add(&self, other: &Self) -> Self;
438
439    /// Scalar multiplication: α · v
440    ///
441    /// Scale the algebra element by a real number.
442    ///
443    /// # Properties
444    ///
445    /// - Distributive: `v.scale(a + b) ≈ v.scale(a).add(&v.scale(b))`
446    /// - Associative: `v.scale(a * b) ≈ v.scale(a).scale(b)`
447    /// - Identity: `v.scale(1.0) == v`
448    #[must_use]
449    fn scale(&self, scalar: f64) -> Self;
450
451    /// Euclidean norm: ||v||
452    ///
453    /// For finite-dimensional vector spaces, this is the standard L² norm:
454    /// ```text
455    /// ||v|| = √(Σᵢ vᵢ²)
456    /// ```
457    ///
458    /// Used for:
459    /// - Gradient descent step size control
460    /// - Checking convergence (||∇f|| < ε)
461    ///
462    /// # Returns
463    ///
464    /// Non-negative real number. Zero iff `self` is the zero element.
465    #[must_use]
466    fn norm(&self) -> f64;
467
468    /// Get the i-th basis element of the Lie algebra.
469    ///
470    /// For su(2), the basis is {σₓ, σᵧ, σᵤ}/2i (Pauli matrices).
471    /// For u(1), the single basis element is i (imaginary unit).
472    ///
473    /// # Parameters
474    ///
475    /// - `i`: Index in [0, `dim()`)
476    ///
477    /// # Panics
478    ///
479    /// If `i >= Self::dim()`.
480    ///
481    /// # Example
482    ///
483    /// ```ignore
484    /// use lie_groups::{LieAlgebra, Su2Algebra};
485    ///
486    /// let sigma_x = Su2Algebra::basis_element(0);
487    /// let sigma_y = Su2Algebra::basis_element(1);
488    /// let sigma_z = Su2Algebra::basis_element(2);
489    /// ```
490    #[must_use]
491    fn basis_element(i: usize) -> Self;
492
493    /// Construct algebra element from basis coordinates.
494    ///
495    /// Given coefficients [c₀, c₁, ..., c_{n-1}], constructs:
496    /// ```text
497    /// v = Σᵢ cᵢ eᵢ
498    /// ```
499    /// where eᵢ are the basis elements.
500    ///
501    /// # Parameters
502    ///
503    /// - `components`: Slice of length `Self::dim()`
504    ///
505    /// # Panics
506    ///
507    /// If `components.len() != Self::dim()`.
508    ///
509    /// # Example
510    ///
511    /// ```ignore
512    /// use lie_groups::{LieAlgebra, Su2Algebra};
513    ///
514    /// let v = Su2Algebra::from_components(&[1.0, 0.0, 0.0]);  // Pure X
515    /// let w = Su2Algebra::from_components(&[0.5, 0.5, 0.0]);  // X + Y mix
516    /// ```
517    #[must_use]
518    fn from_components(components: &[f64]) -> Self;
519
520    /// Extract basis coordinates from algebra element.
521    ///
522    /// Returns the coefficients [c₀, c₁, ..., c_{n-1}] such that:
523    /// ```text
524    /// self = Σᵢ cᵢ eᵢ
525    /// ```
526    /// where eᵢ are the basis elements.
527    ///
528    /// # Returns
529    ///
530    /// Vector of length `Self::dim()` containing the basis coordinates.
531    ///
532    /// # Example
533    ///
534    /// ```ignore
535    /// use lie_groups::{LieAlgebra, Su2Algebra};
536    ///
537    /// let v = Su2Algebra::from_components(&[1.0, 2.0, 3.0]);
538    /// let coords = v.to_components();
539    /// assert_eq!(coords, vec![1.0, 2.0, 3.0]);
540    /// ```
541    #[must_use]
542    fn to_components(&self) -> Vec<f64>;
543
544    /// Lie bracket operation: [X, Y] ∈ 𝔤
545    ///
546    /// The Lie bracket is a binary operation on the Lie algebra that captures
547    /// the non-commutativity of the group. For matrix Lie algebras:
548    /// ```text
549    /// [X, Y] = XY - YX  (matrix commutator)
550    /// ```
551    ///
552    /// # Mathematical Properties
553    ///
554    /// The Lie bracket must satisfy:
555    ///
556    /// 1. **Bilinearity**:
557    ///    - `[aX + bY, Z] = a[X,Z] + b[Y,Z]`
558    ///    - `[X, aY + bZ] = a[X,Y] + b[X,Z]`
559    ///
560    /// 2. **Antisymmetry**: `[X, Y] = -[Y, X]`
561    ///
562    /// 3. **Jacobi Identity**:
563    ///    ```text
564    ///    [X, [Y, Z]] + [Y, [Z, X]] + [Z, [X, Y]] = 0
565    ///    ```
566    ///
567    /// # Geometric Interpretation
568    ///
569    /// The bracket measures the "infinitesimal non-commutativity" of the group.
570    /// For SU(2) with structure constants `[eᵢ, eⱼ] = −εᵢⱼₖeₖ`:
571    /// ```text
572    /// [e₁, e₂] = −e₃,  [e₂, e₃] = −e₁,  [e₃, e₁] = −e₂
573    /// ```
574    ///
575    /// # Applications
576    ///
577    /// - **Structure constants**: Define the algebra's multiplication table
578    /// - **Curvature formulas**: F = dA + [A, A]
579    /// - **Representation theory**: Adjoint representation
580    ///
581    /// # Examples
582    ///
583    /// ```ignore
584    /// use lie_groups::{LieAlgebra, Su2Algebra};
585    ///
586    /// let e1 = Su2Algebra::basis_element(0);
587    /// let e2 = Su2Algebra::basis_element(1);
588    /// let bracket = e1.bracket(&e2);
589    ///
590    /// // [e₁, e₂] = −e₃, so bracket has norm 1
591    /// assert!((bracket.norm() - 1.0).abs() < 1e-10);
592    /// ```
593    ///
594    #[must_use]
595    fn bracket(&self, other: &Self) -> Self;
596
597    /// Inner product: ⟨v, w⟩
598    ///
599    /// The inner product induced by the Killing form (or Frobenius norm for
600    /// matrix Lie algebras). For an orthonormal basis {eᵢ}:
601    /// ```text
602    /// ⟨v, w⟩ = Σᵢ vᵢ wᵢ
603    /// ```
604    ///
605    /// # Numerical Stability
606    ///
607    /// This method provides a numerically stable inner product computation.
608    /// The default implementation uses `to_components()` for direct summation,
609    /// avoiding the catastrophic cancellation issues of the polarization identity.
610    ///
611    /// # Returns
612    ///
613    /// The inner product as a real number. For orthonormal bases, this equals
614    /// the dot product of the coefficient vectors.
615    ///
616    /// # Default Implementation
617    ///
618    /// Computes `Σᵢ self.to_components()[i] * other.to_components()[i]`.
619    /// Override for algebras with more efficient direct computation.
620    #[must_use]
621    fn inner(&self, other: &Self) -> f64 {
622        let v = self.to_components();
623        let w = other.to_components();
624        v.iter().zip(w.iter()).map(|(vi, wi)| vi * wi).sum()
625    }
626}
627
628/// Core trait for Lie group elements.
629///
630/// A Lie group is a smooth manifold with a compatible group structure.
631/// This trait captures both the algebraic (group operations) and geometric
632/// (distance, smoothness) aspects.
633///
634/// # Mathematical Properties
635///
636/// Implementations must satisfy the group axioms:
637///
638/// 1. **Identity**: `g.compose(&G::identity()) == g`
639/// 2. **Inverse**: `g.compose(&g.inverse()) == G::identity()`
640/// 3. **Associativity**: `g1.compose(&g2.compose(&g3)) == g1.compose(&g2).compose(&g3)`
641/// 4. **Closure**: `compose` always produces a valid group element
642///
643/// Additionally, the manifold structure should satisfy:
644///
645/// 5. **Metric**: `distance_to_identity()` measures geodesic distance on the manifold
646/// 6. **Smoothness**: Small parameter changes produce smooth paths in the group
647///
648/// # Type Parameters vs Associated Types
649///
650/// This trait uses `Self` for the group element type, allowing different
651/// representations of the same abstract group (e.g., matrix vs quaternion
652/// representation of SU(2)).
653///
654/// # Examples
655///
656/// ```
657/// use lie_groups::{LieGroup, SU2};
658///
659/// let g = SU2::rotation_x(1.0);
660/// let h = SU2::rotation_y(0.5);
661///
662/// // Group operations
663/// let product = g.compose(&h);
664/// let inv = g.inverse();
665///
666/// // Check group axioms
667/// assert!(g.compose(&g.inverse()).distance_to_identity() < 1e-10);
668/// ```
669///
670/// # Open Trait
671///
672/// This trait is open for implementation. The associated type `Algebra`
673/// must implement `LieAlgebra`, providing compile-time enforcement of
674/// the group-algebra correspondence.
675pub trait LieGroup: Clone + Sized {
676    /// Matrix dimension in the fundamental representation.
677    ///
678    /// For matrix Lie groups, this is the size of the matrices:
679    /// - U(1): `DIM = 1` (complex numbers as 1×1 matrices)
680    /// - SU(2): `DIM = 2` (2×2 unitary matrices)
681    /// - SU(3): `DIM = 3` (3×3 unitary matrices)
682    /// - SO(3): `DIM = 3` (3×3 orthogonal matrices)
683    ///
684    /// # Compile-Time Dimension Safety
685    ///
686    /// This enables the compiler to verify dimension compatibility:
687    ///
688    /// ```ignore
689    /// // ✅ Compiles: Same dimension
690    /// fn compatible<G: LieGroup<DIM = 2>>() {
691    ///     let g1 = SU2::identity();
692    ///     // All operations have compatible dimensions
693    /// }
694    ///
695    /// // ❌ Compile error: Dimension mismatch
696    /// fn incompatible<G1: LieGroup<DIM = 2>, G2: LieGroup<DIM = 3>>() {
697    ///     // Can't mix 2×2 and 3×3 operations
698    /// }
699    /// ```
700    ///
701    /// # Note
702    ///
703    /// This is **not** the dimension of the Lie algebra:
704    /// - dim(SU(2)) = 2 (matrix size)
705    /// - dim(su(2)) = 3 (algebra dimension)
706    const DIM: usize;
707
708    /// Associated Lie algebra type.
709    ///
710    /// The Lie algebra 𝔤 is the tangent space at the identity of the Lie group G.
711    /// This associated type enables generic algorithms that work in the algebra
712    /// (e.g., gradient descent, exponential coordinates).
713    ///
714    /// # Examples
715    ///
716    /// - `SU(2)::Algebra` = `Su2Algebra` (3-dimensional)
717    /// - `U(1)::Algebra` = `U1Algebra` (1-dimensional)
718    /// - `SU(3)::Algebra` = `Su3Algebra` (8-dimensional)
719    type Algebra: LieAlgebra;
720
721    /// The identity element e ∈ G.
722    ///
723    /// Satisfies: `g.compose(&Self::identity()) == g` for all g.
724    ///
725    /// # Mathematical Note
726    ///
727    /// For matrix groups, this is the identity matrix I.
728    /// For additive groups, this would be zero.
729    #[must_use]
730    fn identity() -> Self;
731
732    /// Group composition (multiplication): g₁ · g₂
733    ///
734    /// Computes the product of two group elements. For matrix groups,
735    /// this is matrix multiplication. For SU(2), preserves unitarity.
736    ///
737    /// # Properties
738    ///
739    /// - Associative: `g1.compose(&g2.compose(&g3)) == g1.compose(&g2).compose(&g3)`
740    /// - Identity: `g.compose(&Self::identity()) == g`
741    ///
742    /// # Complexity
743    ///
744    /// | Group | Time | Space | Notes |
745    /// |-------|------|-------|-------|
746    /// | SU(2) via quaternion | O(1) | O(1) | 16 multiplications |
747    /// | SU(N) via matrix | O(N³) | O(N²) | Matrix multiplication |
748    /// | U(1) | O(1) | O(1) | Complex multiplication |
749    ///
750    /// # Examples
751    ///
752    /// ```
753    /// use lie_groups::{LieGroup, SU2};
754    ///
755    /// let g = SU2::rotation_x(0.5);
756    /// let h = SU2::rotation_y(0.3);
757    /// let product = g.compose(&h);
758    /// ```
759    #[must_use]
760    fn compose(&self, other: &Self) -> Self;
761
762    /// Group inverse: g⁻¹
763    ///
764    /// Computes the unique element such that `g.compose(&g.inverse()) == identity`.
765    /// For unitary matrix groups, this is the conjugate transpose (adjoint).
766    ///
767    /// # Properties
768    ///
769    /// - Involutive: `g.inverse().inverse() == g`
770    /// - Inverse property: `g.compose(&g.inverse()) == Self::identity()`
771    ///
772    /// # Complexity
773    ///
774    /// | Group | Time | Space | Notes |
775    /// |-------|------|-------|-------|
776    /// | SU(2) via quaternion | O(1) | O(1) | Quaternion conjugate |
777    /// | SU(N) unitary | O(N²) | O(N²) | Conjugate transpose |
778    /// | U(1) | O(1) | O(1) | Complex conjugate |
779    ///
780    /// # Examples
781    ///
782    /// ```
783    /// use lie_groups::{LieGroup, SU2};
784    ///
785    /// let g = SU2::rotation_z(1.2);
786    /// let g_inv = g.inverse();
787    ///
788    /// // Verify inverse property (up to numerical precision)
789    /// assert!(g.compose(&g_inv).distance_to_identity() < 1e-10);
790    /// ```
791    #[must_use]
792    fn inverse(&self) -> Self;
793
794    /// Adjoint representation element (for matrix groups: conjugate transpose).
795    ///
796    /// For matrix groups: `adjoint(g) = g†` (conjugate transpose)
797    /// For unitary groups: `adjoint(g) = inverse(g)`
798    ///
799    /// # Gauge Theory Application
800    ///
801    /// In gauge transformations, fields transform as:
802    /// ```text
803    /// A' = g A g† + g dg†
804    /// ```
805    /// where g† is the adjoint.
806    ///
807    /// # Examples
808    ///
809    /// ```
810    /// use lie_groups::{LieGroup, SU2};
811    ///
812    /// let g = SU2::rotation_x(0.7);
813    /// let g_adj = g.adjoint();
814    ///
815    /// // For unitary groups, adjoint equals inverse
816    /// assert!(g_adj.compose(&g).distance_to_identity() < 1e-10);
817    /// ```
818    #[must_use]
819    fn adjoint(&self) -> Self;
820
821    /// Adjoint representation: `Ad_g`: 𝔤 → 𝔤
822    ///
823    /// The adjoint representation is a group homomorphism `Ad`: G → Aut(𝔤) that maps
824    /// each group element g to a linear automorphism of the Lie algebra.
825    ///
826    /// # Mathematical Definition
827    ///
828    /// For matrix Lie groups:
829    /// ```text
830    /// Ad_g(X) = g X g⁻¹
831    /// ```
832    ///
833    /// For unitary groups where g† = g⁻¹:
834    /// ```text
835    /// Ad_g(X) = g X g†
836    /// ```
837    ///
838    /// # Distinction from `adjoint()`
839    ///
840    /// - `adjoint()`: Returns g† (conjugate transpose), a **group element**
841    /// - `adjoint_action()`: Returns `Ad_g(X)` (conjugation), a **Lie algebra element**
842    ///
843    /// | Method | Signature | Output Type | Mathematical Meaning |
844    /// |--------|-----------|-------------|---------------------|
845    /// | `adjoint()` | `g → g†` | Group element | Hermitian conjugate |
846    /// | `adjoint_action()` | `(g, X) → gXg⁻¹` | Algebra element | Conjugation action |
847    ///
848    /// # Properties
849    ///
850    /// The adjoint representation is a group homomorphism:
851    /// ```text
852    /// Ad_{g₁g₂}(X) = Ad_{g₁}(Ad_{g₂}(X))
853    /// Ad_e(X) = X
854    /// Ad_g⁻¹(X) = Ad_g⁻¹(X)
855    /// ```
856    ///
857    /// It preserves the Lie bracket:
858    /// ```text
859    /// Ad_g([X, Y]) = [Ad_g(X), Ad_g(Y)]
860    /// ```
861    ///
862    /// For abelian groups (e.g., U(1)):
863    /// ```text
864    /// Ad_g(X) = X  (trivial action)
865    /// ```
866    ///
867    /// # Gauge Theory Application
868    ///
869    /// In gauge transformations, the connection A ∈ 𝔤 transforms as:
870    /// ```text
871    /// A' = Ad_g(A) + g dg†
872    ///    = g A g† + (derivative term)
873    /// ```
874    ///
875    /// The adjoint action captures how gauge fields rotate under symmetry transformations.
876    ///
877    /// # Examples
878    ///
879    /// ```ignore
880    /// use lie_groups::{LieGroup, SU2, Su2Algebra};
881    ///
882    /// let g = SU2::rotation_z(std::f64::consts::PI / 2.0);  // 90° rotation
883    /// let X = Su2Algebra::basis_element(0);  // σ_x direction
884    ///
885    /// // Conjugation rotates the algebra element
886    /// let rotated_X = g.adjoint_action(&X);
887    ///
888    /// // Should now point in σ_y direction (rotated by 90°)
889    /// ```
890    ///
891    /// # Complexity
892    ///
893    /// | Group | Time | Space | Notes |
894    /// |-------|------|-------|-------|
895    /// | SU(2) via quaternion | O(1) | O(1) | Quaternion sandwich: qvq* |
896    /// | SU(N) via matrix | O(N³) | O(N²) | Two matrix multiplications |
897    /// | U(1) | O(1) | O(1) | Trivial (abelian) |
898    /// | SO(3) | O(1) | O(1) | 3×3 matrix rotation |
899    ///
900    /// # See Also
901    ///
902    /// - [`exp`](Self::exp) - Exponential map 𝔤 → G
903    /// - [`adjoint`](Self::adjoint) - Conjugate transpose g → g†
904    /// - [`LieAlgebra::bracket`] - Lie bracket [·,·]
905    #[must_use]
906    fn adjoint_action(&self, algebra_element: &Self::Algebra) -> Self::Algebra;
907
908    /// Geodesic distance from identity: d(g, e)
909    ///
910    /// Measures the "size" of the group element as the length of the
911    /// shortest geodesic curve from the identity to g on the Lie group
912    /// manifold.
913    ///
914    /// # Mathematical Definition
915    ///
916    /// For matrix Lie groups, typically computed as:
917    /// ```text
918    /// d(g, e) = ||log(g)||_F
919    /// ```
920    /// where ||·||_F is the Frobenius norm and log is the matrix logarithm.
921    ///
922    /// For SU(2), this simplifies to:
923    /// ```text
924    /// d(g, e) = arccos(Re(Tr(g))/2)
925    /// ```
926    /// which is the rotation angle.
927    ///
928    /// # Properties
929    ///
930    /// - Non-negative: `d(g, e) ≥ 0`
931    /// - Identity: `d(e, e) = 0`
932    /// - Symmetric: `d(g, e) = d(g⁻¹, e)`
933    ///
934    /// # Returns
935    ///
936    /// Distance in radians (for rotation groups) or appropriate units.
937    ///
938    /// # Examples
939    ///
940    /// ```
941    /// use lie_groups::{LieGroup, SU2};
942    ///
943    /// let identity = SU2::identity();
944    /// assert!(identity.distance_to_identity().abs() < 1e-10);
945    ///
946    /// let rotation = SU2::rotation_x(1.0);
947    /// assert!((rotation.distance_to_identity() - 1.0).abs() < 1e-10);
948    /// ```
949    #[must_use]
950    fn distance_to_identity(&self) -> f64;
951
952    /// Distance between two group elements: d(g, h)
953    ///
954    /// Computed as the distance from the identity to g⁻¹h:
955    /// ```text
956    /// d(g, h) = d(g⁻¹h, e)
957    /// ```
958    ///
959    /// This is the canonical left-invariant metric on the Lie group.
960    ///
961    /// # Default Implementation
962    ///
963    /// Provided via left-invariance of the metric. Can be overridden
964    /// for efficiency if a direct formula is available.
965    #[must_use]
966    fn distance(&self, other: &Self) -> f64 {
967        self.inverse().compose(other).distance_to_identity()
968    }
969
970    /// Check if this element is approximately the identity.
971    ///
972    /// Useful for numerical algorithms to check convergence or
973    /// validate gauge fixing.
974    ///
975    /// # Parameters
976    ///
977    /// - `tolerance`: Maximum allowed distance from identity
978    ///
979    /// # Examples
980    ///
981    /// ```
982    /// use lie_groups::{LieGroup, SU2};
983    ///
984    /// let almost_identity = SU2::rotation_x(1e-12);
985    /// assert!(almost_identity.is_near_identity(1e-10));
986    /// ```
987    #[must_use]
988    fn is_near_identity(&self, tolerance: f64) -> bool {
989        self.distance_to_identity() < tolerance
990    }
991
992    /// Exponential map: 𝔤 → G
993    ///
994    /// Maps elements from the Lie algebra (tangent space at identity) to the Lie group
995    /// via the matrix exponential or equivalent operation.
996    ///
997    /// # Mathematical Definition
998    ///
999    /// For matrix groups:
1000    /// ```text
1001    /// exp(X) = I + X + X²/2! + X³/3! + ...
1002    /// ```
1003    ///
1004    /// For SU(2) with X = θ·n̂·(iσ/2) (rotation by angle θ around axis n̂):
1005    /// ```text
1006    /// exp(X) = cos(θ/2)I + i·sin(θ/2)·(n̂·σ)
1007    /// ```
1008    ///
1009    /// For U(1) with X = iθ:
1010    /// ```text
1011    /// exp(iθ) = e^(iθ)
1012    /// ```
1013    ///
1014    /// # Properties
1015    ///
1016    /// - exp(0) = identity
1017    /// - exp is a local diffeomorphism near 0
1018    /// - exp(t·X) traces a geodesic on the group manifold
1019    ///
1020    /// # Applications
1021    ///
1022    /// - **Optimization**: Gradient descent on manifolds via retraction
1023    /// - **Integration**: Solving ODEs on Lie groups
1024    /// - **Parameterization**: Unconstrained → constrained optimization
1025    ///
1026    /// # Complexity
1027    ///
1028    /// | Group | Time | Space | Notes |
1029    /// |-------|------|-------|-------|
1030    /// | SU(2) via quaternion | O(1) | O(1) | Rodrigues formula |
1031    /// | SU(N) via Padé | O(N³) | O(N²) | Matrix exp via scaling+squaring |
1032    /// | U(1) | O(1) | O(1) | e^(iθ) |
1033    /// | SO(3) via Rodrigues | O(1) | O(1) | Closed-form rotation |
1034    ///
1035    /// # Examples
1036    ///
1037    /// ```ignore
1038    /// use lie_groups::{LieGroup, SU2, Su2Algebra};
1039    ///
1040    /// let tangent = Su2Algebra::from_components(&[1.0, 0.0, 0.0]);
1041    /// let group_element = SU2::exp(&tangent);
1042    /// // group_element is a rotation by 1 radian around X-axis
1043    /// ```
1044    #[must_use]
1045    fn exp(tangent: &Self::Algebra) -> Self;
1046
1047    /// Logarithm map: G → 𝔤 (inverse of exponential)
1048    ///
1049    /// Maps group elements near the identity back to the Lie algebra.
1050    /// This is the **inverse** of the exponential map in a neighborhood of the identity.
1051    ///
1052    /// # Mathematical Definition
1053    ///
1054    /// For g ∈ G sufficiently close to the identity, returns X ∈ 𝔤 such that:
1055    /// ```text
1056    /// exp(X) = g
1057    /// ```
1058    ///
1059    /// For matrix groups, this is the matrix logarithm:
1060    /// ```text
1061    /// log(g) = Σ_{n=1}^∞ (-1)^{n+1} (g - I)^n / n
1062    /// ```
1063    ///
1064    /// For specific groups:
1065    ///
1066    /// - **U(1)**: log(e^{iθ}) = iθ (principal branch)
1067    /// - **SU(2)**: log(exp(θ n̂·σ)) = θ n̂·σ for θ ∈ [0, π)
1068    /// - **SO(3)**: Rodrigues' formula in reverse
1069    ///
1070    /// # Domain Restrictions
1071    ///
1072    /// Unlike `exp` which is defined on all of 𝔤, `log` is only well-defined
1073    /// on a **neighborhood of the identity**:
1074    ///
1075    /// - **U(1)**: All elements except negative real axis
1076    /// - **SU(2)**: All elements except -I (rotation by π with ambiguous axis)
1077    /// - **SU(3)**: Elements with all eigenvalues in a small neighborhood
1078    ///
1079    /// # Errors
1080    ///
1081    /// Returns `Err(LogError::NotNearIdentity)` if the element is too far from identity.
1082    /// Returns `Err(LogError::Singularity)` if at an exact singularity (e.g., -I for SU(2)).
1083    ///
1084    /// # Gauge Theory Application
1085    ///
1086    /// In lattice gauge theory, the **curvature** (field strength) is computed
1087    /// from the **holonomy** (Wilson loop) via the logarithm:
1088    ///
1089    /// ```text
1090    /// F_□ = log(U_□) / a²
1091    /// ```
1092    ///
1093    /// where:
1094    /// - U_□ ∈ G is the plaquette holonomy (group element)
1095    /// - F_□ ∈ 𝔤 is the curvature (Lie algebra element)
1096    /// - a is the lattice spacing
1097    ///
1098    /// This is the **discrete analog** of the continuum formula:
1099    /// ```text
1100    /// F_{μν} = ∂_μ A_ν - ∂_ν A_μ + [A_μ, A_ν] ∈ 𝔤
1101    /// ```
1102    ///
1103    /// # Numerical Considerations
1104    ///
1105    /// - For elements very close to identity, use Taylor expansion of log
1106    /// - For elements near singularities, logarithm is numerically unstable
1107    /// - In lattice gauge theory, use smaller lattice spacing if log fails
1108    ///
1109    /// # Complexity
1110    ///
1111    /// | Group | Time | Space | Notes |
1112    /// |-------|------|-------|-------|
1113    /// | SU(2) via quaternion | O(1) | O(1) | atan2 + normalize |
1114    /// | SU(N) via eigendecomp | O(N³) | O(N²) | Schur decomposition |
1115    /// | U(1) | O(1) | O(1) | atan2 |
1116    /// | SO(3) via Rodrigues | O(1) | O(1) | Closed-form |
1117    ///
1118    /// # Properties
1119    ///
1120    /// - log(identity) = 0
1121    /// - log(exp(X)) = X for small X (in the cut)
1122    /// - log(g^{-1}) = -log(g) for g near identity
1123    /// - log is a local diffeomorphism near the identity
1124    ///
1125    /// # Examples
1126    ///
1127    /// ```ignore
1128    /// use lie_groups::{LieGroup, SU2, Su2Algebra};
1129    ///
1130    /// // Create rotation around X-axis by 0.5 radians
1131    /// let tangent = Su2Algebra::from_components(&[0.5, 0.0, 0.0]);
1132    /// let g = SU2::exp(&tangent);
1133    ///
1134    /// // Recover the algebra element
1135    /// let recovered = g.log().unwrap();
1136    ///
1137    /// // Should match original (up to numerical precision)
1138    /// assert!(tangent.add(&recovered.scale(-1.0)).norm() < 1e-10);
1139    /// ```
1140    ///
1141    /// # See Also
1142    ///
1143    /// - [`exp`](Self::exp) - Exponential map 𝔤 → G
1144    /// - [`LogError`](crate::LogError) - Error types for logarithm failures
1145    fn log(&self) -> crate::error::LogResult<Self::Algebra>;
1146
1147    /// Dimension of the fundamental representation.
1148    ///
1149    /// For matrix Lie groups, this is the size of the matrices:
1150    /// - U(1): 1 (complex numbers as 1×1 matrices)
1151    /// - SU(2): 2 (2×2 unitary matrices with det=1)
1152    /// - SU(3): 3 (3×3 unitary matrices with det=1)
1153    /// - SO(3): 3 (3×3 orthogonal matrices with det=1)
1154    ///
1155    /// **Note**: This is different from the dimension of the Lie algebra!
1156    /// - dim(SU(2)) = 2 (matrix size)
1157    /// - dim(su(2)) = 3 (algebra dimension)
1158    ///
1159    /// # Relationship to `DIM` Const
1160    ///
1161    /// This method returns the same value as the `DIM` associated constant,
1162    /// but is available at runtime. Prefer using `G::DIM` for compile-time
1163    /// dimension checking when possible.
1164    ///
1165    /// # Uses
1166    ///
1167    /// - Computing Yang-Mills action: S = Σ (dim - Re Tr(F))
1168    /// - Normalizing traces and invariants
1169    /// - Checking unitarity constraints
1170    ///
1171    /// # Examples
1172    ///
1173    /// ```ignore
1174    /// use lie_groups::{LieGroup, SU2, U1};
1175    ///
1176    /// assert_eq!(SU2::dim(), 2);
1177    /// assert_eq!(SU2::DIM, 2);  // Same value, compile-time
1178    /// assert_eq!(U1::dim(), 1);
1179    /// ```
1180    #[must_use]
1181    fn dim() -> usize {
1182        Self::DIM // Default implementation
1183    }
1184
1185    /// Trace in the fundamental representation.
1186    ///
1187    /// For matrix groups, computes Tr(M) = Σᵢ Mᵢᵢ (sum of diagonal elements).
1188    ///
1189    /// # Mathematical Properties
1190    ///
1191    /// For unitary matrices U:
1192    /// - Tr(U) is complex with |Tr(U)| ≤ dim
1193    /// - Tr(I) = dim (identity has maximal trace)
1194    /// - Tr(U†) = Tr(U)* (trace of adjoint is complex conjugate)
1195    /// - Tr(UV) = Tr(VU) (cyclicity)
1196    ///
1197    /// # Physical Significance
1198    ///
1199    /// In Yang-Mills theory, the action uses traces of field strength:
1200    /// ```text
1201    /// S = -1/(4g²) ∫ Tr(F_μν F^μν) d⁴x
1202    /// ```
1203    ///
1204    /// On the lattice:
1205    /// ```text
1206    /// S = β Σ_p (dim - Re Tr(U_p))
1207    /// ```
1208    /// where `U_p` is the plaquette holonomy.
1209    ///
1210    /// # Examples
1211    ///
1212    /// ```ignore
1213    /// use lie_groups::{LieGroup, SU2};
1214    /// use num_complex::Complex;
1215    ///
1216    /// let identity = SU2::identity();
1217    /// let tr = identity.trace();
1218    /// assert!((tr - Complex::new(2.0, 0.0)).norm() < 1e-10);  // Tr(I₂) = 2
1219    ///
1220    /// let rotation = SU2::rotation_x(std::f64::consts::PI);
1221    /// let tr_rot = rotation.trace();
1222    /// // Rotation by π has Tr = 0
1223    /// assert!(tr_rot.norm() < 1e-10);
1224    /// ```
1225    #[must_use]
1226    fn trace(&self) -> Complex<f64>;
1227
1228    /// Trace of the identity element
1229    ///
1230    /// For SU(N), SO(N), and related matrix groups, the identity matrix I has trace N.
1231    /// This is used in gauge theory computations:
1232    /// - Yang-Mills action: `S_p = N - Re Tr(U_p)` (measures deviation from identity)
1233    /// - Field strength: `|F| ∝ √(N - Re Tr(U))`
1234    ///
1235    /// # Examples
1236    ///
1237    /// ```
1238    /// use lie_groups::{SU2, SU3, LieGroup};
1239    ///
1240    /// assert_eq!(SU2::trace_identity(), 2.0);
1241    /// assert_eq!(SU3::trace_identity(), 3.0);
1242    /// ```
1243    #[must_use]
1244    fn trace_identity() -> f64 {
1245        Self::DIM as f64
1246    }
1247
1248    /// Project element back onto the group manifold using Gram-Schmidt orthogonalization.
1249    ///
1250    /// **Critical for numerical stability** (Tao priority): After many `compose()` or `exp()` operations,
1251    /// floating-point errors accumulate and the result may drift from the group manifold.
1252    /// This method projects the element back onto the manifold, restoring group constraints.
1253    ///
1254    /// # Mathematical Background
1255    ///
1256    /// For matrix Lie groups, the group manifold is defined by constraints:
1257    /// - **SU(N)**: U†U = I (unitary) and det(U) = 1 (special)
1258    /// - **SO(N)**: R†R = I (orthogonal) and det(R) = +1 (special)
1259    ///
1260    /// Gram-Schmidt reorthogonalization ensures U†U = I by:
1261    /// 1. Orthogonalizing columns via modified Gram-Schmidt
1262    /// 2. Rescaling det(U) → 1 if needed
1263    ///
1264    /// # When to Use
1265    ///
1266    /// Call after:
1267    /// - Long chains: `g₁.compose(&g₂).compose(&g₃)....compose(&gₙ)` for large n
1268    /// - Many exponentials: `exp(X₁).compose(&exp(X₂))....`
1269    /// - Scaling-and-squaring: After matrix powers in `exp()` algorithm
1270    ///
1271    /// # Performance vs Accuracy Trade-off
1272    ///
1273    /// - **Cost**: O(N³) for N×N matrices (Gram-Schmidt)
1274    /// - **Benefit**: Prevents catastrophic error accumulation
1275    /// - **Guideline**: Reorthogonalize every ~100 operations in long chains
1276    ///
1277    /// # Example
1278    ///
1279    /// ```ignore
1280    /// use lie_groups::{LieGroup, SU2};
1281    ///
1282    /// // Long composition chain
1283    /// let mut g = SU2::identity();
1284    /// for i in 0..1000 {
1285    ///     g = g.compose(&SU2::rotation_x(0.01));
1286    ///     if i % 100 == 0 {
1287    ///         g = g.reorthogonalize();  // Prevent drift every 100 steps
1288    ///     }
1289    /// }
1290    ///
1291    /// // Verify still on manifold
1292    /// assert!(g.adjoint().compose(&g).distance_to_identity() < 1e-12);
1293    /// ```
1294    ///
1295    /// # Default Implementation
1296    ///
1297    /// The default implementation uses the exponential/logarithm round-trip:
1298    /// ```text
1299    /// reorthogonalize(g) = exp(log(g))
1300    /// ```
1301    ///
1302    /// This works because:
1303    /// 1. `log(g)` extracts the Lie algebra element (always exact algebraically)
1304    /// 2. `exp(X)` produces an element exactly on the manifold
1305    ///
1306    /// Groups may override this with more efficient methods (e.g., direct Gram-Schmidt for SU(N)).
1307    ///
1308    /// # Error Handling
1309    ///
1310    /// - **Never fails** for elements close to the manifold
1311    /// - For elements far from manifold (catastrophic numerical error), returns best approximation
1312    /// - If `log()` fails (element too far from identity), falls back to identity
1313    ///
1314    /// # See Also
1315    ///
1316    /// - [`exp`](Self::exp) - Exponential map (always produces elements on manifold)
1317    /// - [`log`](Self::log) - Logarithm map (inverse of exp near identity)
1318    /// - [Modified Gram-Schmidt Algorithm](https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process)
1319    ///
1320    /// # References
1321    ///
1322    /// - Tao, "Topics in Random Matrix Theory" (2012) - Numerical stability
1323    /// - Higham, "Accuracy and Stability of Numerical Algorithms" (2002) - Matrix orthogonalization
1324    #[must_use]
1325    fn reorthogonalize(&self) -> Self {
1326        // Default: Round-trip through log-exp
1327        // This works because exp() always produces elements on the manifold
1328        match self.log() {
1329            Ok(algebra) => Self::exp(&algebra),
1330            Err(_) => {
1331                // Fallback: If log fails (element too corrupted), return identity
1332                Self::identity()
1333            }
1334        }
1335    }
1336}
1337
1338#[cfg(test)]
1339mod tests {
1340    use super::*;
1341
1342    /// Mock Lie algebra for testing (u(1) ≅ ℝ)
1343    #[derive(Clone, Debug, PartialEq)]
1344    struct U1Algebra {
1345        value: f64,
1346    }
1347
1348    impl LieAlgebra for U1Algebra {
1349        const DIM: usize = 1;
1350
1351        fn zero() -> Self {
1352            Self { value: 0.0 }
1353        }
1354
1355        fn add(&self, other: &Self) -> Self {
1356            Self {
1357                value: self.value + other.value,
1358            }
1359        }
1360
1361        fn scale(&self, scalar: f64) -> Self {
1362            Self {
1363                value: self.value * scalar,
1364            }
1365        }
1366
1367        fn norm(&self) -> f64 {
1368            self.value.abs()
1369        }
1370
1371        fn basis_element(i: usize) -> Self {
1372            assert_eq!(i, 0, "U(1) algebra is 1-dimensional");
1373            Self { value: 1.0 }
1374        }
1375
1376        fn from_components(components: &[f64]) -> Self {
1377            assert_eq!(components.len(), 1);
1378            Self {
1379                value: components[0],
1380            }
1381        }
1382
1383        fn to_components(&self) -> Vec<f64> {
1384            vec![self.value]
1385        }
1386
1387        fn bracket(&self, _other: &Self) -> Self {
1388            Self::zero() // u(1) is abelian
1389        }
1390    }
1391
1392    /// Mock Lie group for testing trait laws
1393    #[derive(Clone, Debug, PartialEq)]
1394    struct U1 {
1395        angle: f64, // U(1) = circle group, element = e^{iθ}
1396    }
1397
1398    impl LieGroup for U1 {
1399        const DIM: usize = 1;
1400
1401        type Algebra = U1Algebra;
1402
1403        fn identity() -> Self {
1404            Self { angle: 0.0 }
1405        }
1406
1407        fn compose(&self, other: &Self) -> Self {
1408            Self {
1409                angle: (self.angle + other.angle) % (2.0 * std::f64::consts::PI),
1410            }
1411        }
1412
1413        fn inverse(&self) -> Self {
1414            Self {
1415                angle: (-self.angle).rem_euclid(2.0 * std::f64::consts::PI),
1416            }
1417        }
1418
1419        fn adjoint(&self) -> Self {
1420            self.inverse() // U(1) is abelian, so adjoint = inverse
1421        }
1422
1423        fn adjoint_action(&self, algebra_element: &Self::Algebra) -> Self::Algebra {
1424            // Trivial adjoint action for abelian group
1425            algebra_element.clone()
1426        }
1427
1428        fn distance_to_identity(&self) -> f64 {
1429            // Shortest arc distance on circle [0, 2π]
1430            let normalized = self.angle.rem_euclid(2.0 * std::f64::consts::PI);
1431            let dist = normalized.min(2.0 * std::f64::consts::PI - normalized);
1432            dist.abs()
1433        }
1434
1435        fn exp(tangent: &Self::Algebra) -> Self {
1436            // exp(iθ) = e^(iθ), represented as angle θ
1437            Self {
1438                angle: tangent.value.rem_euclid(2.0 * std::f64::consts::PI),
1439            }
1440        }
1441
1442        fn log(&self) -> crate::error::LogResult<Self::Algebra> {
1443            // For U(1), log(e^{iθ}) = iθ
1444            Ok(U1Algebra { value: self.angle })
1445        }
1446
1447        fn dim() -> usize {
1448            1 // U(1) is 1-dimensional (complex numbers as 1×1 matrices)
1449        }
1450
1451        fn trace(&self) -> Complex<f64> {
1452            // Trace of e^(iθ) viewed as 1×1 matrix
1453            Complex::new(self.angle.cos(), self.angle.sin())
1454        }
1455    }
1456
1457    #[test]
1458    fn test_identity_law() {
1459        let g = U1 { angle: 1.0 };
1460        let e = U1::identity();
1461
1462        let g_times_e = g.compose(&e);
1463        assert!((g_times_e.angle - g.angle).abs() < 1e-10);
1464    }
1465
1466    #[test]
1467    fn test_inverse_law() {
1468        let g = U1 { angle: 2.5 };
1469        let g_inv = g.inverse();
1470        let product = g.compose(&g_inv);
1471
1472        assert!(product.is_near_identity(1e-10));
1473    }
1474
1475    #[test]
1476    fn test_associativity() {
1477        let g1 = U1 { angle: 0.5 };
1478        let g2 = U1 { angle: 1.2 };
1479        let g3 = U1 { angle: 0.8 };
1480
1481        let left = g1.compose(&g2.compose(&g3));
1482        let right = g1.compose(&g2).compose(&g3);
1483
1484        assert!((left.angle - right.angle).abs() < 1e-10);
1485    }
1486
1487    #[test]
1488    fn test_distance_symmetry() {
1489        let g = U1 { angle: 1.5 };
1490        let g_inv = g.inverse();
1491
1492        let d1 = g.distance_to_identity();
1493        let d2 = g_inv.distance_to_identity();
1494
1495        assert!((d1 - d2).abs() < 1e-10);
1496    }
1497
1498    #[test]
1499    fn test_is_near_identity() {
1500        let e = U1::identity();
1501        assert!(e.is_near_identity(1e-10));
1502
1503        let g = U1 { angle: 1e-12 };
1504        assert!(g.is_near_identity(1e-10));
1505
1506        let h = U1 { angle: 0.1 };
1507        assert!(!h.is_near_identity(1e-10));
1508    }
1509
1510    #[test]
1511    fn test_exponential_map() {
1512        // exp(0) = identity
1513        let zero = U1Algebra::zero();
1514        let exp_zero = U1::exp(&zero);
1515        assert!(exp_zero.is_near_identity(1e-10));
1516
1517        // exp(iθ) = e^(iθ)
1518        let tangent = U1Algebra { value: 1.5 };
1519        let group_elem = U1::exp(&tangent);
1520        assert!((group_elem.angle - 1.5).abs() < 1e-10);
1521    }
1522
1523    #[test]
1524    fn test_dimension() {
1525        assert_eq!(U1::dim(), 1);
1526    }
1527
1528    #[test]
1529    fn test_trace() {
1530        // Tr(identity) = dim
1531        let e = U1::identity();
1532        let tr_e = e.trace();
1533        assert!((tr_e - Complex::new(1.0, 0.0)).norm() < 1e-10);
1534
1535        // Tr(e^(iπ)) = e^(iπ) = -1
1536        let g = U1 {
1537            angle: std::f64::consts::PI,
1538        };
1539        let tr_g = g.trace();
1540        assert!((tr_g - Complex::new(-1.0, 0.0)).norm() < 1e-10);
1541    }
1542
1543    #[test]
1544    fn test_lie_algebra_zero() {
1545        let zero = U1Algebra::zero();
1546        assert_eq!(zero.value, 0.0);
1547    }
1548
1549    #[test]
1550    fn test_lie_algebra_add() {
1551        let v = U1Algebra { value: 1.0 };
1552        let w = U1Algebra { value: 2.0 };
1553        let sum = v.add(&w);
1554        assert_eq!(sum.value, 3.0);
1555    }
1556
1557    #[test]
1558    fn test_lie_algebra_scale() {
1559        let v = U1Algebra { value: 2.0 };
1560        let scaled = v.scale(3.0);
1561        assert_eq!(scaled.value, 6.0);
1562    }
1563
1564    #[test]
1565    fn test_lie_algebra_norm() {
1566        let v = U1Algebra { value: -3.0 };
1567        assert_eq!(v.norm(), 3.0);
1568    }
1569
1570    #[test]
1571    fn test_lie_algebra_basis() {
1572        let basis = U1Algebra::basis_element(0);
1573        assert_eq!(basis.value, 1.0);
1574    }
1575
1576    #[test]
1577    fn test_lie_algebra_from_components() {
1578        let v = U1Algebra::from_components(&[4.5]);
1579        assert_eq!(v.value, 4.5);
1580    }
1581
1582    #[test]
1583    fn test_const_dim_available_at_compile_time() {
1584        // Verify that DIM const is accessible
1585        assert_eq!(U1::DIM, 1);
1586
1587        // Verify dim() method returns same value
1588        assert_eq!(U1::dim(), U1::DIM);
1589    }
1590
1591    // Note: Associated const equality (e.g., `LieGroup<DIM = 1>`) is not yet stable
1592    // See https://github.com/rust-lang/rust/issues/92827
1593    //
1594    // Once stabilized, we can write functions like:
1595    //
1596    // ```ignore
1597    // fn dimension_one_only<G: LieGroup<DIM = 1>>() -> usize {
1598    //     G::DIM
1599    // }
1600    // ```
1601    //
1602    // This would enable compile-time verification of dimension compatibility.
1603
1604    // ========================================================================
1605    // Reorthogonalization Tests (TAO PRIORITY - Numerical Stability)
1606    // ========================================================================
1607
1608    /// Test that reorthogonalization fixes drift in long composition chains
1609    #[test]
1610    fn test_reorthogonalize_prevents_drift_su2() {
1611        use crate::SU2;
1612
1613        // Create a long composition chain without reorthogonalization
1614        let mut g_no_reorth = SU2::identity();
1615        for _ in 0..1000 {
1616            g_no_reorth = g_no_reorth.compose(&SU2::rotation_x(0.01));
1617        }
1618
1619        // Check unitarity: U†U should be identity
1620        let unitarity_error_no_reorth = g_no_reorth
1621            .adjoint()
1622            .compose(&g_no_reorth)
1623            .distance_to_identity();
1624
1625        // Same chain WITH reorthogonalization every 100 steps
1626        let mut g_with_reorth = SU2::identity();
1627        for i in 0..1000 {
1628            g_with_reorth = g_with_reorth.compose(&SU2::rotation_x(0.01));
1629            if i % 100 == 0 {
1630                g_with_reorth = g_with_reorth.reorthogonalize();
1631            }
1632        }
1633
1634        let unitarity_error_with_reorth = g_with_reorth
1635            .adjoint()
1636            .compose(&g_with_reorth)
1637            .distance_to_identity();
1638
1639        // Reorthogonalization should reduce drift significantly
1640        assert!(
1641            unitarity_error_with_reorth < unitarity_error_no_reorth,
1642            "Reorthogonalization should reduce drift: {} vs {}",
1643            unitarity_error_with_reorth,
1644            unitarity_error_no_reorth
1645        );
1646
1647        // With reorthogonalization, should stay very close to manifold
1648        // Tolerance allows for numerical variation across library versions
1649        assert!(
1650            unitarity_error_with_reorth < 1e-7,
1651            "With reorthogonalization, drift should be minimal: {}",
1652            unitarity_error_with_reorth
1653        );
1654    }
1655
1656    /// Test that `reorthogonalize()` is idempotent
1657    #[test]
1658    fn test_reorthogonalize_idempotent() {
1659        use crate::SU2;
1660        use std::f64::consts::PI;
1661
1662        let g = SU2::rotation_y(PI / 3.0);
1663
1664        let g1 = g.reorthogonalize();
1665        let g2 = g1.reorthogonalize();
1666
1667        // Applying twice should give same result (idempotent)
1668        assert!(
1669            g1.distance(&g2) < 1e-14,
1670            "Reorthogonalization should be idempotent"
1671        );
1672    }
1673
1674    /// Test reorthogonalization preserves group element
1675    #[test]
1676    fn test_reorthogonalize_preserves_element() {
1677        use crate::SU2;
1678        use std::f64::consts::PI;
1679
1680        let g = SU2::rotation_z(PI / 4.0);
1681        let g_reorth = g.reorthogonalize();
1682
1683        // Should be very close to original
1684        assert!(
1685            g.distance(&g_reorth) < 1e-12,
1686            "Reorthogonalization should preserve element"
1687        );
1688
1689        // Reorthogonalized version should be exactly on manifold
1690        let unitarity_error = g_reorth.adjoint().compose(&g_reorth).distance_to_identity();
1691        assert!(
1692            unitarity_error < 1e-14,
1693            "Reorthogonalized element should be on manifold: {}",
1694            unitarity_error
1695        );
1696    }
1697
1698    // ========================================================================
1699    // Operator overloading tests
1700    // ========================================================================
1701
1702    use crate::so3::{So3Algebra, SO3};
1703    use crate::su2::{Su2Algebra, SU2};
1704    use crate::su3::Su3Algebra;
1705
1706    #[test]
1707    fn test_su2_algebra_operators() {
1708        let x = Su2Algebra([1.0, 2.0, 3.0]);
1709        let y = Su2Algebra([0.5, 1.0, 1.5]);
1710
1711        // Add
1712        let sum = x + y;
1713        assert_eq!(sum.0, [1.5, 3.0, 4.5]);
1714
1715        // Add with references
1716        let sum_ref = &x + &y;
1717        assert_eq!(sum_ref.0, [1.5, 3.0, 4.5]);
1718
1719        // Sub
1720        let diff = x - y;
1721        assert_eq!(diff.0, [0.5, 1.0, 1.5]);
1722
1723        // Neg
1724        let neg = -x;
1725        assert_eq!(neg.0, [-1.0, -2.0, -3.0]);
1726
1727        // Scalar mul (both directions)
1728        let scaled = x * 2.0;
1729        assert_eq!(scaled.0, [2.0, 4.0, 6.0]);
1730        let scaled2 = 2.0 * x;
1731        assert_eq!(scaled2.0, [2.0, 4.0, 6.0]);
1732    }
1733
1734    #[test]
1735    fn test_su2_group_mul_operator() {
1736        let g = SU2::rotation_x(0.3);
1737        let h = SU2::rotation_y(0.7);
1738
1739        // Operator should match compose
1740        let product_op = &g * &h;
1741        let product_compose = g.compose(&h);
1742        assert_eq!(product_op.matrix, product_compose.matrix);
1743
1744        // MulAssign
1745        let mut g2 = g.clone();
1746        g2 *= &h;
1747        assert_eq!(g2.matrix, product_compose.matrix);
1748    }
1749
1750    #[test]
1751    fn test_so3_algebra_operators() {
1752        let x = So3Algebra([1.0, 0.0, 0.0]);
1753        let y = So3Algebra([0.0, 1.0, 0.0]);
1754
1755        let sum = x + y;
1756        assert_eq!(sum.0, [1.0, 1.0, 0.0]);
1757
1758        let scaled = 3.0 * x;
1759        assert_eq!(scaled.0, [3.0, 0.0, 0.0]);
1760
1761        let neg = -y;
1762        assert_eq!(neg.0, [0.0, -1.0, 0.0]);
1763    }
1764
1765    #[test]
1766    fn test_so3_group_mul_operator() {
1767        let r1 = SO3::rotation_x(0.5);
1768        let r2 = SO3::rotation_z(0.3);
1769        let product = &r1 * &r2;
1770        let expected = r1.compose(&r2);
1771        assert!(product.distance(&expected) < 1e-14);
1772    }
1773
1774    #[test]
1775    fn test_u1_algebra_operators() {
1776        let x = crate::U1Algebra(1.5);
1777        let y = crate::U1Algebra(0.5);
1778
1779        assert_eq!((x + y).0, 2.0);
1780        assert_eq!((x - y).0, 1.0);
1781        assert_eq!((-x).0, -1.5);
1782        assert_eq!((x * 3.0).0, 4.5);
1783        assert_eq!((3.0 * x).0, 4.5);
1784    }
1785
1786    #[test]
1787    fn test_u1_group_mul_operator() {
1788        let g = crate::U1::from_angle(1.0);
1789        let h = crate::U1::from_angle(2.0);
1790        let product = &g * &h;
1791        let expected = g.compose(&h);
1792        assert!(product.distance(&expected) < 1e-14);
1793    }
1794
1795    #[test]
1796    fn test_rplus_algebra_operators() {
1797        let x = crate::RPlusAlgebra(2.0);
1798        let y = crate::RPlusAlgebra(0.5);
1799
1800        assert_eq!((x + y).0, 2.5);
1801        assert_eq!((x - y).0, 1.5);
1802        assert_eq!((-x).0, -2.0);
1803        assert_eq!((x * 4.0).0, 8.0);
1804        assert_eq!((4.0 * x).0, 8.0);
1805    }
1806
1807    #[test]
1808    fn test_su3_algebra_operators() {
1809        let x = Su3Algebra([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
1810        let y = Su3Algebra([0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
1811
1812        let sum = x + y;
1813        assert_eq!(sum.0[0], 1.0);
1814        assert_eq!(sum.0[1], 1.0);
1815
1816        let scaled = 2.0 * x;
1817        assert_eq!(scaled.0[0], 2.0);
1818    }
1819
1820    #[test]
1821    fn test_sun_algebra_operators() {
1822        use crate::sun::SunAlgebra;
1823
1824        let x = SunAlgebra::<2>::basis_element(0);
1825        let y = SunAlgebra::<2>::basis_element(1);
1826
1827        let sum = &x + &y;
1828        assert_eq!(sum.coefficients, vec![1.0, 1.0, 0.0]);
1829
1830        let diff = x.clone() - y.clone();
1831        assert_eq!(diff.coefficients, vec![1.0, -1.0, 0.0]);
1832
1833        let neg = -x.clone();
1834        assert_eq!(neg.coefficients, vec![-1.0, 0.0, 0.0]);
1835
1836        let scaled = x.clone() * 5.0;
1837        assert_eq!(scaled.coefficients, vec![5.0, 0.0, 0.0]);
1838
1839        let scaled2 = 5.0 * x;
1840        assert_eq!(scaled2.coefficients, vec![5.0, 0.0, 0.0]);
1841    }
1842
1843    #[test]
1844    fn test_sun_group_mul_operator() {
1845        use crate::sun::SUN;
1846
1847        let g = SUN::<3>::identity();
1848        let h = SUN::<3>::identity();
1849        let product = &g * &h;
1850        assert!(product.distance_to_identity() < 1e-14);
1851    }
1852
1853    #[test]
1854    fn test_algebra_operator_consistency_with_trait_methods() {
1855        // Verify that operators produce the same results as trait methods
1856        let x = Su2Algebra([0.3, -0.7, 1.2]);
1857        let y = Su2Algebra([0.5, 0.1, -0.4]);
1858
1859        // operator + should equal LieAlgebra::add
1860        let op_sum = x + y;
1861        let trait_sum = LieAlgebra::add(&x, &y);
1862        assert_eq!(op_sum.0, trait_sum.0);
1863
1864        // operator * scalar should equal LieAlgebra::scale
1865        let op_scaled = x * 2.5;
1866        let trait_scaled = x.scale(2.5);
1867        assert_eq!(op_scaled.0, trait_scaled.0);
1868    }
1869}