Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- run: cargo generate-lockfile
- uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -90,4 +91,4 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: EmbarkStudios/cargo-deny-action@34899fc7ba81ca6268d5947a7a16b4649013fea1 # v2
- uses: EmbarkStudios/cargo-deny-action@44db170f6a7d12a6e90340e9e0fca1f650d34b14 # v2.0.15
13 changes: 12 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lie-groups"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
rust-version = "1.75"
authors = ["Gestell Labs LLC"]
Expand Down Expand Up @@ -28,3 +28,14 @@ proptest = { version = "1", optional = true }
[dev-dependencies]
rand = "0.8"
rand_distr = "0.4"

[[example]]
name = "basics"
required-features = ["rand"]

[[example]]
name = "random_sampling"
required-features = ["rand"]

[[example]]
name = "bch_vs_exp"
10 changes: 5 additions & 5 deletions deny.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[advisories]
vulnerability = "deny"
unmaintained = "warn"
yanked = "warn"
notice = "warn"
version = 2
ignore = [
"RUSTSEC-2024-0436", # paste: unmaintained but stable, transitive via nalgebra
]

[licenses]
unlicensed = "deny"
version = 2
allow = [
"MIT",
"Apache-2.0",
Expand Down
56 changes: 56 additions & 0 deletions examples/basics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Basic Lie group operations in 30 lines.
//!
//! Run: `cargo run --example basics`

use lie_groups::prelude::*;

fn main() {
// Group elements
let g = SU2::rotation_x(0.5);
let h = SU2::rotation_y(0.3);
println!("g = {}", g);
println!("h = {}", h);

// Composition via * operator
let product = &g * &h;
println!("g·h = {}", product);

// Inverse
let g_inv = g.inverse();
let should_be_identity = g_inv.compose(&g);
println!(
"g⁻¹·g = {} (distance from I: {:.2e})",
should_be_identity,
should_be_identity.distance_to_identity()
);

// Exponential map: algebra → group
let x = Su2Algebra::new([0.1, 0.2, 0.3]);
let exp_x = SU2::exp(&x);
println!("exp({:?}) = {}", x, exp_x);

// Logarithm: group → algebra
let log_g = g.log().unwrap();
println!("log(g) = {:?}", log_g);

// Compose a path of rotations
let path: Vec<SU2> = (0..10).map(|i| SU2::rotation_z(0.1 * i as f64)).collect();
let holonomy: SU2 = path.iter().product();
println!("holonomy = {}", holonomy);

// BCH formula: log(exp(X)·exp(Y)) ≈ X + Y + ½[X,Y] + ...
let y = Su2Algebra::new([0.05, -0.1, 0.0]);
let bch = bch_checked::<Su2Algebra>(&x, &y, 5).unwrap();
println!("BCH(X,Y) = {:?}", bch);

// Works generically over any Lie group
fn parallel_transport<G: LieGroup + for<'a> std::iter::Product<&'a G>>(path: &[G]) -> G {
path.iter().product()
}
let u1_path = vec![
U1::from_angle(0.1),
U1::from_angle(0.2),
U1::from_angle(0.3),
];
println!("U(1) holonomy = {}", parallel_transport(&u1_path));
}
50 changes: 50 additions & 0 deletions examples/bch_vs_exp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! Verify the Baker-Campbell-Hausdorff formula against direct exp·exp.
//!
//! The BCH formula gives: log(exp(X)·exp(Y)) = X + Y + ½[X,Y] + ...
//! This example compares the truncated series against the exact answer.
//!
//! Run: `cargo run --example bch_vs_exp`

use lie_groups::prelude::*;

fn main() {
println!("=== BCH formula vs direct computation ===\n");

// Small algebra elements (BCH converges well)
let x = Su2Algebra::new([0.1, 0.0, 0.0]);
let y = Su2Algebra::new([0.0, 0.1, 0.0]);

// Direct: log(exp(X) · exp(Y))
let direct = SU2::exp(&x) * SU2::exp(&y);
let z_exact = direct.log().unwrap();

// BCH approximation at various orders
for order in [2, 3, 4, 5] {
let z_bch = lie_groups::bch_safe::<SU2>(&x, &y, order).unwrap();
let error = z_exact.add(&z_bch.scale(-1.0)).norm();
println!("Order {}: error = {:.2e}", order, error);
}

// bch_checked with highest order
let z_best = lie_groups::bch_safe::<SU2>(&x, &y, 5).unwrap();
let error_best = z_exact.add(&z_best.scale(-1.0)).norm();
println!("Best(5): error = {:.2e}", error_best);

// Show the bracket structure
println!("\n=== Lie bracket structure constants ===\n");
for i in 0..3 {
for j in (i + 1)..3 {
let ei = Su2Algebra::basis_element(i);
let ej = Su2Algebra::basis_element(j);
let bracket = ei.bracket(&ej);
println!("[e{}, e{}] = {:?}", i, j, bracket.to_components());
}
}

// SU(3) bracket
println!("\n=== SU(3) brackets ===\n");
let t1 = Su3Algebra::basis_element(0);
let t2 = Su3Algebra::basis_element(1);
let bracket_12 = t1.bracket(&t2);
println!("[T1, T2] = {:?}", bracket_12.to_components());
}
55 changes: 55 additions & 0 deletions examples/random_sampling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! Sample random group elements and verify statistical properties.
//!
//! Run: `cargo run --example random_sampling`

use lie_groups::prelude::*;
use rand::SeedableRng;

fn main() {
let mut rng = rand::rngs::StdRng::seed_from_u64(42);

// Sample from each compact group
println!("=== Random Haar sampling ===\n");

// SU(2)
let g2 = SU2::random_haar(&mut rng);
println!("SU(2): {}", g2);
println!(" unitary: {}", g2.verify_unitarity(1e-10));

// SO(3)
let r = SO3::random_haar(&mut rng);
println!("SO(3): {}", r);
println!(" orthogonal: {}", r.verify_orthogonality(1e-10));

// SU(3) — the QCD gauge group
let g3 = SU3::random_haar(&mut rng);
println!("SU(3): {}", g3);
println!(" unitary: {}", g3.verify_unitarity(1e-10));

// SU(4) — Pati-Salam model
let g4 = SU4::random_haar(&mut rng);
println!("SU(4): {}", g4);
println!(" unitary: {}", g4.verify_unitarity(1e-10));

// Verify Haar measure: average trace should be 0 for SU(N), N≥2
println!("\n=== Haar measure check: <tr(U)> → 0 ===\n");
let n_samples = 10_000;

let avg_trace_su2: f64 = (0..n_samples)
.map(|_| SU2::random_haar(&mut rng).trace().re)
.sum::<f64>()
/ n_samples as f64;
println!("SU(2): <Re tr(U)> = {:.6} (expect ~0)", avg_trace_su2);

let avg_trace_su3: f64 = (0..n_samples)
.map(|_| SU3::random_haar(&mut rng).trace().re)
.sum::<f64>()
/ n_samples as f64;
println!("SU(3): <Re tr(U)> = {:.6} (expect ~0)", avg_trace_su3);

let avg_trace_sun4: f64 = (0..n_samples)
.map(|_| SUN::<4>::random_haar(&mut rng).trace().re)
.sum::<f64>()
/ n_samples as f64;
println!("SU(4): <Re tr(U)> = {:.6} (expect ~0)", avg_trace_sun4);
}
17 changes: 12 additions & 5 deletions src/bch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1237,8 +1237,10 @@ mod tests {
/// BCH vs exp·log on SUN<3> with non-axis-aligned inputs.
#[test]
fn test_bch_vs_exp_log_sun3() {
let x = SunAlgebra::<3>::from_components(&[0.05, -0.03, 0.02, 0.04, -0.01, 0.03, -0.02, 0.01]);
let y = SunAlgebra::<3>::from_components(&[-0.02, 0.04, -0.03, 0.01, 0.05, -0.04, 0.02, -0.03]);
let x =
SunAlgebra::<3>::from_components(&[0.05, -0.03, 0.02, 0.04, -0.01, 0.03, -0.02, 0.01]);
let y =
SunAlgebra::<3>::from_components(&[-0.02, 0.04, -0.03, 0.01, 0.05, -0.04, 0.02, -0.03]);

let direct = SUN::<3>::exp(&x).compose(&SUN::<3>::exp(&y));
let bch_z = bch_fifth_order(&x, &y);
Expand Down Expand Up @@ -1281,8 +1283,10 @@ mod tests {
#[test]
fn test_bch_su3_vs_sun3_consistency() {
// Build algebra elements via SU3, convert to SUN<3> via matrix roundtrip
let x_su3 = Su3Algebra::from_components(&[0.05, -0.03, 0.02, 0.04, -0.01, 0.03, -0.02, 0.01]);
let y_su3 = Su3Algebra::from_components(&[-0.02, 0.04, -0.03, 0.01, 0.05, -0.04, 0.02, -0.03]);
let x_su3 =
Su3Algebra::from_components(&[0.05, -0.03, 0.02, 0.04, -0.01, 0.03, -0.02, 0.01]);
let y_su3 =
Su3Algebra::from_components(&[-0.02, 0.04, -0.03, 0.01, 0.05, -0.04, 0.02, -0.03]);

let x_sun = SunAlgebra::<3>::from_matrix(&x_su3.to_matrix());
let y_sun = SunAlgebra::<3>::from_matrix(&y_su3.to_matrix());
Expand All @@ -1301,7 +1305,10 @@ mod tests {
assert!(
(m_su3[(r, c)] - m_sun[(r, c)]).norm() < 1e-12,
"BCH matrix disagrees at ({},{}): SU3={}, SUN<3>={}",
r, c, m_su3[(r, c)], m_sun[(r, c)]
r,
c,
m_su3[(r, c)],
m_sun[(r, c)]
);
}
}
Expand Down
Loading
Loading