use std::sync::Arc;
use super::{cgc, shared_directproduct, Irrep, SunError};
use crate::frcore::{
self, f_block_raw, f_unitarity_residual, hexagon_residual, pentagon_residual, r_block_raw,
Family, MEntry,
};
pub use crate::frcore::{FBlock, RBlock};
struct SunFamily;
impl Family for SunFamily {
type Irrep = Irrep;
type Error = SunError;
fn mult(&mut self, a: &Irrep, b: &Irrep, c: &Irrep) -> Result<usize, SunError> {
mult(a, b, c)
}
fn cgc_entries(&mut self, a: &Irrep, b: &Irrep, c: &Irrep) -> Result<Vec<MEntry>, SunError> {
Ok(cgc(a, b, c)?
.entries()
.iter()
.map(|e| MEntry {
m1: e.m1,
m2: e.m2,
m3: e.m3,
mu: e.mu,
value: e.value,
})
.collect())
}
fn products(&mut self, a: &Irrep, b: &Irrep) -> Result<Vec<Irrep>, SunError> {
Ok(shared_directproduct(a, b)?
.iter()
.map(|(irrep, _)| irrep.clone())
.collect())
}
}
fn mult(a: &Irrep, b: &Irrep, c: &Irrep) -> Result<usize, SunError> {
if c.rank() != a.rank() {
return Err(SunError::RankMismatch {
a: a.rank(),
b: c.rank(),
});
}
Ok(shared_directproduct(a, b)?.multiplicity(c) as usize)
}
fn require_same_rank(labels: &[&Irrep]) -> Result<(), SunError> {
let n = labels[0].rank();
for s in &labels[1..] {
if s.rank() != n {
return Err(SunError::RankMismatch { a: n, b: s.rank() });
}
}
Ok(())
}
pub fn f_symbol(
a: &Irrep,
b: &Irrep,
c: &Irrep,
d: &Irrep,
e: &Irrep,
f: &Irrep,
) -> Result<FBlock, SunError> {
require_same_rank(&[a, b, c, d, e, f])?;
let vertices = [
(a, b, e), (e, c, d), (b, c, f), (a, f, d), ];
for (x, y, z) in vertices {
if mult(x, y, z)? == 0 {
return Err(SunError::ZeroFusionChannel {
a: x.dynkin(),
b: y.dynkin(),
c: z.dynkin(),
});
}
}
let cache = crate::cache::cache_sun_f();
let key = (
a.clone(),
b.clone(),
c.clone(),
d.clone(),
e.clone(),
f.clone(),
);
if let Some(hit) = cache.get(&key) {
return Ok((*hit).clone());
}
let block = f_block_raw(&mut SunFamily, a, b, c, d, e, f)?;
let stored = cache.insert(key, Arc::new(block));
Ok((*stored).clone())
}
pub fn r_symbol(a: &Irrep, b: &Irrep, c: &Irrep) -> Result<RBlock, SunError> {
require_same_rank(&[a, b, c])?;
if mult(a, b, c)? == 0 {
return Err(SunError::ZeroFusionChannel {
a: a.dynkin(),
b: b.dynkin(),
c: c.dynkin(),
});
}
r_block_raw(&mut SunFamily, a, b, c)
}
pub fn check_f_unitarity(a: &Irrep, b: &Irrep, c: &Irrep, d: &Irrep) -> Result<(), SunError> {
require_same_rank(&[a, b, c, d])?;
let worst = f_unitarity_residual(&mut SunFamily, a, b, c, d)?;
if worst > frcore::TOL_F_UNITARY {
return Err(SunError::FNotUnitary { residual: worst });
}
Ok(())
}
pub fn check_pentagon(a: &Irrep, b: &Irrep, c: &Irrep, d: &Irrep) -> Result<(), SunError> {
require_same_rank(&[a, b, c, d])?;
let worst = pentagon_residual(&mut SunFamily, a, b, c, d)?;
if worst > frcore::TOL_PENTAGON {
return Err(SunError::PentagonViolation { residual: worst });
}
Ok(())
}
pub fn check_hexagon(a: &Irrep, b: &Irrep, c: &Irrep) -> Result<(), SunError> {
require_same_rank(&[a, b, c])?;
let worst = hexagon_residual(&mut SunFamily, a, b, c)?;
if worst > frcore::TOL_HEXAGON {
return Err(SunError::HexagonViolation { residual: worst });
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn irr(d: &[i64]) -> Irrep {
Irrep::from_dynkin(d).unwrap()
}
#[test]
fn private_product_consumers_do_not_reconstruct_public_maps() {
crate::sun::reset_public_directproduct_reconstructions();
let trivial = Irrep::trivial(3).unwrap();
let three = irr(&[1, 0]);
let six = irr(&[2, 0]);
let mut family = SunFamily;
assert_eq!(family.mult(&trivial, &three, &three).unwrap(), 1);
assert_eq!(
family.products(&trivial, &three).unwrap().as_slice(),
std::slice::from_ref(&three)
);
let _ = f_symbol(&trivial, &three, &three, &six, &three, &six).unwrap();
assert_eq!(crate::sun::public_directproduct_reconstructions(), 0);
let _ = crate::sun::directproduct(&trivial, &three).unwrap();
assert_eq!(crate::sun::public_directproduct_reconstructions(), 1);
}
#[test]
fn f_symbol_zero_vertex_is_typed_error() {
let three = irr(&[1, 0]);
let eight = irr(&[1, 1]);
let err = f_symbol(&three, &three, &three, &three, &eight, &three).unwrap_err();
assert!(matches!(err, SunError::ZeroFusionChannel { .. }));
}
#[test]
fn f_symbol_rank_mismatch_is_typed_error() {
let su3 = irr(&[1, 0]);
let su4 = irr(&[1, 0, 0]);
let err = f_symbol(&su3, &su3, &su3, &su3, &su4, &su3).unwrap_err();
assert!(matches!(err, SunError::RankMismatch { .. }));
}
#[test]
fn r_symbol_zero_vertex_is_typed_error() {
let three = irr(&[1, 0]);
let eight = irr(&[1, 1]);
let err = r_symbol(&three, &three, &eight).unwrap_err();
assert!(matches!(err, SunError::ZeroFusionChannel { .. }));
}
#[test]
fn r_symbol_rank_mismatch_is_typed_error() {
let su3 = irr(&[1, 0]);
let su4 = irr(&[1, 0, 0]);
let err = r_symbol(&su3, &su4, &su3).unwrap_err();
assert!(matches!(err, SunError::RankMismatch { .. }));
}
#[test]
fn su3_trivial_f_is_scalar_one() {
let triv = Irrep::trivial(3).unwrap();
let three = irr(&[1, 0]);
let six = irr(&[2, 0]);
let block = f_symbol(&triv, &three, &three, &six, &three, &six).unwrap();
assert_eq!(block.dims(), [1, 1, 1, 1]);
assert!((block.at(0, 0, 0, 0) - 1.0).abs() < 1e-12);
}
#[test]
fn su3_octet_cubed_f_block_is_2x2x2x2() {
let eight = irr(&[1, 1]);
let block = f_symbol(&eight, &eight, &eight, &eight, &eight, &eight).unwrap();
assert_eq!(block.dims(), [2, 2, 2, 2]);
}
#[test]
fn su3_octet_r_block_is_2x2() {
let eight = irr(&[1, 1]);
let block = r_symbol(&eight, &eight, &eight).unwrap();
assert_eq!(block.dim(), 2);
}
#[test]
fn su3_f_unitarity_multiplicity_free() {
let three = irr(&[1, 0]);
check_f_unitarity(&three, &three, &three, &irr(&[1, 0])).unwrap();
}
#[test]
fn su3_f_unitarity_with_multiplicity() {
let eight = irr(&[1, 1]);
check_f_unitarity(&eight, &eight, &eight, &eight).unwrap();
}
#[test]
fn su3_pentagon_multiplicity_free() {
let three = irr(&[1, 0]);
check_pentagon(&three, &three, &three, &three).unwrap();
}
#[test]
fn su3_hexagon_multiplicity_free() {
let three = irr(&[1, 0]);
check_hexagon(&three, &three, &three).unwrap();
}
}