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:
redclawsystems
2026-05-19 04:39:49 +00:00
commit f4b75db2ee
291 changed files with 130230 additions and 0 deletions
+200
View File
@@ -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}"
);
}
}