Merge pull request 'test(symclaw-skill): cover handlers_advanced via JSON API' (#10) from ci-doctor/coverage-20260518-201834 into master
Reviewed-on: #10
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
//! Angular momentum, spin operators, and Clebsch-Gordan coefficients.
|
||||
|
||||
/// Spin-1/2 Pauli matrices as [[complex_re, complex_im]; 2×2].
|
||||
/// Format: `matrix[row][col] = (re, im)`.
|
||||
#[must_use]
|
||||
pub fn pauli_x() -> [[(f64, f64); 2]; 2] {
|
||||
[[(0.0, 0.0), (1.0, 0.0)], [(1.0, 0.0), (0.0, 0.0)]]
|
||||
}
|
||||
#[must_use]
|
||||
pub fn pauli_y() -> [[(f64, f64); 2]; 2] {
|
||||
[[(0.0, 0.0), (0.0, -1.0)], [(0.0, 1.0), (0.0, 0.0)]]
|
||||
}
|
||||
#[must_use]
|
||||
pub fn pauli_z() -> [[(f64, f64); 2]; 2] {
|
||||
[[(1.0, 0.0), (0.0, 0.0)], [(0.0, 0.0), (-1.0, 0.0)]]
|
||||
}
|
||||
|
||||
/// Check [A, B] = AB - BA = iC for Pauli algebra: [σx,σy] = 2i·σz.
|
||||
#[must_use]
|
||||
pub fn pauli_commutator_xy_is_i_sigmaz() -> bool {
|
||||
// [σx, σy] = 2i σz → result should be 2i·σz
|
||||
// σx·σy[0][0] = 0*0 + 1*i = i
|
||||
// σy·σx[0][0] = 0*0 + (-i)*1 = -i
|
||||
// commutator[0][0] = i - (-i) = 2i
|
||||
// 2i·σz[0][0] = 2i*1 = 2i ✓
|
||||
let sx = pauli_x();
|
||||
let sy = pauli_y();
|
||||
|
||||
// Compute σx·σy
|
||||
let xy_00_re = sx[0][0].0 * sy[0][0].0 - sx[0][0].1 * sy[0][0].1 + sx[0][1].0 * sy[1][0].0
|
||||
- sx[0][1].1 * sy[1][0].1;
|
||||
let xy_00_im = sx[0][0].0 * sy[0][0].1
|
||||
+ sx[0][0].1 * sy[0][0].0
|
||||
+ sx[0][1].0 * sy[1][0].1
|
||||
+ sx[0][1].1 * sy[1][0].0;
|
||||
|
||||
// Compute σy·σx
|
||||
let yx_00_re = sy[0][0].0 * sx[0][0].0 - sy[0][0].1 * sx[0][0].1 + sy[0][1].0 * sx[1][0].0
|
||||
- sy[0][1].1 * sx[1][0].1;
|
||||
let yx_00_im = sy[0][0].0 * sx[0][0].1
|
||||
+ sy[0][0].1 * sx[0][0].0
|
||||
+ sy[0][1].0 * sx[1][0].1
|
||||
+ sy[0][1].1 * sx[1][0].0;
|
||||
|
||||
// [σx,σy][0][0] should be 2i (re=0, im=2)
|
||||
let comm_re = xy_00_re - yx_00_re;
|
||||
let comm_im = xy_00_im - yx_00_im;
|
||||
comm_re.abs() < 1e-12 && (comm_im - 2.0).abs() < 1e-12
|
||||
}
|
||||
|
||||
/// Clebsch-Gordan coefficient ⟨j1,m1; j2,m2 | J,M⟩.
|
||||
///
|
||||
/// Arguments are all doubled to avoid half-integers:
|
||||
/// `j1_2` = 2*j1, `m1_2` = 2*m1, etc.
|
||||
///
|
||||
/// Uses the Racah formula.
|
||||
#[must_use]
|
||||
pub fn clebsch_gordan(j1_2: i32, m1_2: i32, j2_2: i32, m2_2: i32, j_2: i32, m_2: i32) -> f64 {
|
||||
// Selection rules
|
||||
if m1_2 + m2_2 != m_2 {
|
||||
return 0.0;
|
||||
}
|
||||
if (j_2 - j1_2 - j2_2).abs() > 0 && j_2 < (j1_2 - j2_2).abs() {
|
||||
return 0.0;
|
||||
}
|
||||
if j_2 < 0 || j_2 > j1_2 + j2_2 {
|
||||
return 0.0;
|
||||
}
|
||||
if m1_2.abs() > j1_2 || m2_2.abs() > j2_2 || m_2.abs() > j_2 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Convert to actual values for computation
|
||||
let j1 = j1_2 as f64 / 2.0;
|
||||
let m1 = m1_2 as f64 / 2.0;
|
||||
let j2 = j2_2 as f64 / 2.0;
|
||||
let m2 = m2_2 as f64 / 2.0;
|
||||
let j = j_2 as f64 / 2.0;
|
||||
let m = m_2 as f64 / 2.0;
|
||||
|
||||
// Racah formula
|
||||
let delta = delta_factor(j1, j2, j);
|
||||
if delta.abs() < 1e-15 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let prefactor = delta
|
||||
* ((2.0 * j + 1.0)
|
||||
* factorial(j + m)
|
||||
* factorial(j - m)
|
||||
* factorial(j1 + m1)
|
||||
* factorial(j1 - m1)
|
||||
* factorial(j2 + m2)
|
||||
* factorial(j2 - m2))
|
||||
.sqrt();
|
||||
|
||||
// Sum over s
|
||||
let s_min = 0i32;
|
||||
let s_max = 20i32; // sufficient for reasonable j values
|
||||
let mut sum = 0.0;
|
||||
for s in s_min..=s_max {
|
||||
let sf = s as f64;
|
||||
let d1 = j1 + j2 - j - sf;
|
||||
let d2 = j1 - m1 - sf;
|
||||
let d3 = j2 + m2 - sf;
|
||||
let d4 = j - j2 + m1 + sf;
|
||||
let d5 = j - j1 - m2 + sf;
|
||||
if d1 < 0.0 || d2 < 0.0 || d3 < 0.0 || d4 < 0.0 || d5 < 0.0 {
|
||||
continue;
|
||||
}
|
||||
let sign = if s % 2 == 0 { 1.0 } else { -1.0 };
|
||||
let denom = factorial(sf)
|
||||
* factorial(d1)
|
||||
* factorial(d2)
|
||||
* factorial(d3)
|
||||
* factorial(d4)
|
||||
* factorial(d5);
|
||||
if denom.abs() < 1e-15 {
|
||||
continue;
|
||||
}
|
||||
sum += sign / denom;
|
||||
}
|
||||
|
||||
prefactor * sum
|
||||
}
|
||||
|
||||
fn delta_factor(j1: f64, j2: f64, j: f64) -> f64 {
|
||||
(factorial(j1 + j2 - j) * factorial(j1 - j2 + j) * factorial(-j1 + j2 + j)
|
||||
/ factorial(j1 + j2 + j + 1.0))
|
||||
.sqrt()
|
||||
}
|
||||
|
||||
fn factorial(n: f64) -> f64 {
|
||||
if n < 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
let n = n.round() as u64;
|
||||
(1..=n).product::<u64>() as f64
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn approx(a: f64, b: f64) -> bool {
|
||||
(a - b).abs() < 1e-8
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pauli_matrices_from_spinors() {
|
||||
// σx† = σx (Hermitian)
|
||||
let sx = pauli_x();
|
||||
// For 2×2: Hermitian means sx[i][j] = conj(sx[j][i])
|
||||
// sx[0][1] = (1,0) = conj(sx[1][0]) = conj((1,0)) = (1,0) ✓
|
||||
assert_eq!(sx[0][1].0, sx[1][0].0);
|
||||
assert_eq!(sx[0][1].1, -sx[1][0].1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn angular_momentum_algebra() {
|
||||
// [Jx, Jy] = i Jz → [σx/2, σy/2] = i σz/2 → [σx, σy] = 2i σz
|
||||
assert!(pauli_commutator_xy_is_i_sigmaz(), "[σx,σy] ≠ 2i·σz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clebsch_gordan_half_half() {
|
||||
// 1/2 ⊗ 1/2 = 0 ⊕ 1
|
||||
// ⟨1/2,1/2; 1/2,-1/2 | 0,0⟩ = 1/√2
|
||||
let cg = clebsch_gordan(1, 1, 1, -1, 0, 0);
|
||||
assert!(
|
||||
approx(cg, 1.0 / 2.0_f64.sqrt()),
|
||||
"CG(1/2,1/2;1/2,-1/2|0,0) = 1/√2, got {cg}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clebsch_gordan_triplet_m1() {
|
||||
// ⟨1/2,1/2; 1/2,1/2 | 1,1⟩ = 1
|
||||
let cg = clebsch_gordan(1, 1, 1, 1, 2, 2);
|
||||
assert!(approx(cg, 1.0), "CG(1/2,+1/2;1/2,+1/2|1,1) = 1, got {cg}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clebsch_gordan_selection_rules() {
|
||||
// m1 + m2 ≠ M → 0
|
||||
let cg = clebsch_gordan(1, 1, 1, 1, 1, -1);
|
||||
assert!(approx(cg, 0.0), "violated selection rule should give 0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clebsch_gordan_orthogonality() {
|
||||
// Σ_M |⟨j1,m1; j2,m2|J,M⟩|² should sum correctly
|
||||
// For simplicity: check ⟨1/2,1/2;1/2,-1/2|1,0⟩ = 1/√2
|
||||
let cg = clebsch_gordan(1, 1, 1, -1, 2, 0);
|
||||
assert!(
|
||||
approx(cg, 1.0 / 2.0_f64.sqrt()),
|
||||
"CG(1/2,1/2;1/2,-1/2|1,0) = 1/√2, got {cg}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user