use super::*;
use crate::bcd::{CanonicalCatalog, Series};
use crate::frcore::Family;
use std::sync::atomic::Ordering;
fn irr(s: Series, d: &[i64]) -> Irrep {
Irrep::from_dynkin(s, d).unwrap()
}
#[test]
fn warm_state_does_not_resweep() {
crate::cache::reset();
let mut cat = CanonicalCatalog::new(Series::C, 2).unwrap();
let v = irr(Series::C, &[0, 1]);
let c1 = irr(Series::C, &[2, 0]);
let c2 = irr(Series::C, &[0, 2]);
let sweeps_before = CGC_SWEEPS.load(Ordering::Relaxed);
{
let mut fam = BcdFamily { cat: &mut cat };
fam.cgc_entries(&v, &v, &c1).unwrap(); }
let tier = crate::cache::cache_bcd_cgc();
assert!(
tier.get(&(v.clone(), v.clone(), c1.clone())).is_some(),
"requested channel must be cached"
);
assert!(
tier.get(&(v.clone(), v.clone(), c2.clone())).is_some(),
"the OTHER channel of the same product must be cached by the same sweep \
(no per-channel re-sweep)"
);
assert!(CGC_SWEEPS.load(Ordering::Relaxed) > sweeps_before);
}
#[test]
fn f_symbol_zero_vertex_is_typed_error() {
let mut cat = CanonicalCatalog::new(Series::C, 2).unwrap();
let v = irr(Series::C, &[0, 1]);
let adj = irr(Series::C, &[2, 0]);
let err = f_symbol(&mut cat, &v, &v, &v, &v, &v, &adj).unwrap_err();
assert!(
matches!(
err,
FrError::Catalog(crate::bcd::CatalogError::ZeroFusionChannel { .. })
),
"got {err:?}"
);
}
#[test]
fn f_symbol_foreign_group_is_typed_error() {
let mut cat = CanonicalCatalog::new(Series::C, 2).unwrap();
let foreign = irr(Series::B, &[0, 2]);
let v = irr(Series::C, &[0, 1]);
let err = f_symbol(&mut cat, &foreign, &v, &v, &v, &v, &v).unwrap_err();
assert!(
matches!(
err,
FrError::Catalog(crate::bcd::CatalogError::WrongGroup { .. })
),
"got {err:?}"
);
}
#[test]
fn f_symbol_wrong_rank_is_typed_error() {
let mut cat = CanonicalCatalog::new(Series::C, 2).unwrap();
let wrong_rank = irr(Series::C, &[0, 1, 0]);
let v = irr(Series::C, &[0, 1]);
let err = f_symbol(&mut cat, &wrong_rank, &v, &v, &v, &v, &v).unwrap_err();
assert!(
matches!(
err,
FrError::Catalog(crate::bcd::CatalogError::WrongGroup { .. })
),
"got {err:?}"
);
}
#[test]
fn r_symbol_zero_vertex_is_typed_error() {
let mut cat = CanonicalCatalog::new(Series::C, 2).unwrap();
let v = irr(Series::C, &[0, 1]);
let err = r_symbol(&mut cat, &v, &v, &v).unwrap_err();
assert!(
matches!(
err,
FrError::Catalog(crate::bcd::CatalogError::ZeroFusionChannel { .. })
),
"got {err:?}"
);
}
#[test]
fn r_symbol_foreign_group_is_typed_error() {
let mut cat = CanonicalCatalog::new(Series::B, 2).unwrap();
let foreign = irr(Series::C, &[0, 1]);
let err = r_symbol(&mut cat, &foreign, &foreign, &foreign).unwrap_err();
assert!(
matches!(
err,
FrError::Catalog(crate::bcd::CatalogError::WrongGroup { .. })
),
"got {err:?}"
);
}
#[test]
fn c2_vector_cubed_f_is_multiplicity_free_scalar() {
let mut cat = CanonicalCatalog::new(Series::C, 2).unwrap();
let triv = Irrep::trivial(Series::C, 2).unwrap();
let v = irr(Series::C, &[0, 1]);
let adj = irr(Series::C, &[2, 0]); let block = f_symbol(&mut cat, &triv, &v, &v, &adj, &v, &adj).unwrap();
assert_eq!(block.dims(), [1, 1, 1, 1]);
assert!((block.at(0, 0, 0, 0) - 1.0).abs() < 1e-9);
}
#[test]
fn d3_adjoint_square_84_channel_cgc_is_coherent_or_bricks() {
use crate::bcd::{directproduct, CatalogError};
let g = irr(Series::D, &[0, 1, 1]);
assert_eq!(
directproduct(&g, &g).unwrap().get(&g).copied().unwrap(),
2,
"exact layer must predict OM=2 for the D3 adjoint square"
);
let mut cat = CanonicalCatalog::new(Series::D, 3).unwrap();
let eightyfour = irr(Series::D, &[0, 2, 2]);
match cat.cgc(&g, &g, &eightyfour) {
Ok(_) => {}
Err(CatalogError::BasisIncoherent {
irrep,
product,
residual,
}) => {
assert_eq!(irrep, vec![0, 2, 2]);
assert_eq!(product, (vec![0, 1, 1], vec![0, 1, 1]));
assert!(
residual > 1e-3,
"residual {residual} must be O(1), not noise"
);
}
other => panic!("expected Ok or BasisIncoherent, got {other:?}"),
}
}
#[test]
fn d3_adjoint_f_symbol_closes_or_bricks() {
use crate::bcd::CatalogError;
let g = irr(Series::D, &[0, 1, 1]);
let mut cat = CanonicalCatalog::new(Series::D, 3).unwrap();
match f_symbol(&mut cat, &g, &g, &g, &g, &g, &g) {
Ok(_) => {}
Err(FrError::Catalog(CatalogError::BasisIncoherent { .. })) => {}
other => panic!("expected Ok or BasisIncoherent, got {other:?}"),
}
}
#[test]
fn f_symbol_second_call_is_cache_hit() {
crate::cache::reset();
let mut cat = CanonicalCatalog::new(Series::C, 2).unwrap();
let triv = Irrep::trivial(Series::C, 2).unwrap();
let v = irr(Series::C, &[0, 1]);
let adj = irr(Series::C, &[2, 0]);
let first = f_symbol(&mut cat, &triv, &v, &v, &adj, &v, &adj).unwrap();
let before = crate::cache::stats().hits;
let second = f_symbol(&mut cat, &triv, &v, &v, &adj, &v, &adj).unwrap();
let after = crate::cache::stats().hits;
assert_eq!(first, second);
assert_eq!(
after,
before + 1,
"second call must be served from the cache"
);
}