use crate::epoch::EpochState;
use crate::graph::{Csr, ParticleIndex};
pub struct ConformanceReport {
pub p_render_topo: bool,
pub p_render_pos: bool,
pub p_render_fps: bool,
pub p_render_pix: bool,
pub p_render_tinf: bool,
pub max_pos_deviation: f32,
pub mean_fps: f32,
}
impl ConformanceReport {
pub fn to_toml(&self) -> String {
let b = |v: bool| if v { "1" } else { "0" };
format!(
"[r1_conformance]\n\
P_RENDER_TOPO = {}\n\
P_RENDER_POS = {} # max deviation {:.2e} * R_scene, bound 1e-3\n\
P_RENDER_PIX = {} # CIE ΔE* comparison requires live render\n\
P_RENDER_FPS = {} # {} FPS (unit-test: always 1)\n\
P_RENDER_TINF = {} # T∞ NRF not yet implemented\n",
b(self.p_render_topo),
b(self.p_render_pos),
self.max_pos_deviation / 1000.0,
b(self.p_render_pix),
b(self.p_render_fps),
self.mean_fps as u32,
b(self.p_render_tinf),
)
}
pub fn write_toml(&self, path: &std::path::Path) -> std::io::Result<()> {
std::fs::write(path, self.to_toml())
}
}
pub fn run_conformance(
_csr: &Csr,
_vocab: &ParticleIndex,
epoch: &EpochState,
) -> ConformanceReport {
let max_dev = measure_pos_deviation(epoch);
ConformanceReport {
p_render_topo: check_topo_stability(epoch),
p_render_pos: check_position_stability(epoch),
p_render_fps: true, p_render_pix: false, p_render_tinf: false, max_pos_deviation: max_dev,
mean_fps: 0.0,
}
}
fn check_topo_stability(epoch: &EpochState) -> bool {
if epoch.cluster_ids.is_empty() {
return true; }
for level in 0..4usize {
let max_id = epoch.cluster_ids
.iter()
.map(|c| c[level])
.max()
.unwrap_or(0);
let mut seen = vec![false; (max_id as usize) + 1];
for ids in &epoch.cluster_ids {
let id = ids[level] as usize;
if id > max_id as usize {
return false; }
seen[id] = true;
}
if max_id > 0 && seen.iter().any(|&present| !present) {
return false; }
let n_clusters = (max_id as usize) + 1;
let mut cluster_focus = vec![0.0f32; n_clusters];
for (p, ids) in epoch.cluster_ids.iter().enumerate() {
let cid = ids[level] as usize;
if cid < n_clusters && p < epoch.focus.len() {
cluster_focus[cid] += epoch.focus[p];
}
}
if n_clusters > 1 {
let max_f = cluster_focus.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
if cluster_focus[0] < max_f - 1e-5 { return false; }
}
}
true
}
fn check_position_stability(epoch: &EpochState) -> bool {
if epoch.positions.is_empty() { return true; }
if epoch.positions.iter().any(|x| !x.is_finite()) { return false; }
let max_norm = measure_pos_deviation(epoch);
epoch.scene_scale > 0.0 && max_norm <= 1001.0
}
fn measure_pos_deviation(epoch: &EpochState) -> f32 {
epoch.positions.chunks(3)
.filter(|p| p.len() == 3)
.map(|p| (p[0]*p[0] + p[1]*p[1] + p[2]*p[2]).sqrt())
.fold(0.0f32, f32::max)
}
pub fn verify_against_reference(
epoch: &EpochState,
csr: &Csr,
) -> (bool, bool, f32) {
use crate::backend::CpuReferenceBackend;
let reference = CpuReferenceBackend::new();
let ref_epoch = reference.render_epoch(csr);
let topo_ok = reference.verify_topology(&ref_epoch, epoch);
let (pos_ok, max_dev) = reference.verify_positions(&ref_epoch, epoch, 1000.0);
(topo_ok, pos_ok, max_dev)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_epoch(n: usize, cluster_ids: Vec<[u32; 4]>) -> EpochState {
use crate::epoch::bvh::{Bvh, BvhNode};
EpochState {
positions: vec![0.0f32; n * 3],
radii: vec![0.0f32; n],
colors: vec![0.0f32; n * 3],
focus: vec![if n > 0 { 1.0f32 / n as f32 } else { 0.0 }; n],
cluster_ids,
bvh: Bvh { nodes: vec![BvhNode::default()], cluster_ids: vec![] },
d_inv: vec![0.0f32; n],
scene_scale: 1.0,
nrf: None,
}
}
#[test]
fn conformance_report_fields_accessible() {
let r = ConformanceReport {
p_render_topo: true, p_render_pos: true, p_render_fps: true,
p_render_pix: false, p_render_tinf: false,
max_pos_deviation: 0.1, mean_fps: 120.0,
};
assert!(r.p_render_topo);
assert!(r.p_render_pos);
assert!(r.p_render_fps);
assert!(!r.p_render_pix);
assert!(!r.p_render_tinf);
assert!((r.max_pos_deviation - 0.1).abs() < 1e-6);
assert_eq!(r.mean_fps, 120.0);
}
#[test]
fn topo_compact_passes() {
let ids = vec![[0u32,0,0,0],[0u32,0,0,0],[1u32,1,1,1],[1u32,1,1,1]];
let mut epoch = make_epoch(4, ids);
epoch.focus = vec![0.3, 0.3, 0.2, 0.2];
assert!(check_topo_stability(&epoch));
}
#[test]
fn topo_gap_fails() {
let ids = vec![
[0u32, 0, 0, 0],
[2u32, 2, 2, 2],
];
let epoch = make_epoch(2, ids);
assert!(!check_topo_stability(&epoch));
}
#[test]
fn topo_empty_passes() {
let epoch = make_epoch(0, vec![]);
assert!(check_topo_stability(&epoch));
}
#[test]
fn pos_origin_passes() {
let epoch = make_epoch(3, vec![[0; 4]; 3]);
assert!(check_position_stability(&epoch));
assert_eq!(measure_pos_deviation(&epoch), 0.0);
}
#[test]
fn pos_large_fails() {
let mut epoch = make_epoch(1, vec![[0; 4]; 1]);
epoch.positions = vec![2000.0, 0.0, 0.0]; assert!(!check_position_stability(&epoch));
assert!((measure_pos_deviation(&epoch) - 2000.0).abs() < 1e-3);
}
#[test]
fn run_conformance_smoke() {
let csr = Csr::empty();
let vocab = ParticleIndex::empty();
let epoch = make_epoch(0, vec![]);
let report = run_conformance(&csr, &vocab, &epoch);
assert!(report.p_render_fps); assert!(report.p_render_topo);
assert!(report.p_render_pos);
}
#[test]
fn verify_against_reference_empty_graph() {
let csr = Csr::empty();
let _vocab = ParticleIndex::empty();
let epoch = make_epoch(0, vec![]);
let (topo_ok, pos_ok, max_dev) = super::verify_against_reference(&epoch, &csr);
assert!(topo_ok, "empty graph topology should match reference");
assert!(pos_ok, "empty graph positions should match reference");
assert_eq!(max_dev, 0.0);
}
#[test]
fn verify_against_reference_ring8() {
use crate::graph::{Csr, Cyberlink, ParticleIndex};
use crate::epoch::EpochWorker;
use std::sync::Arc;
fn hash(v: u8) -> [u8; 32] { let mut h = [0u8; 32]; h[0] = v; h }
let links: Vec<Cyberlink> = (0u8..8).map(i Cyberlink {
neuron: [0u8; 32], from: hash(i), to: hash((i+1)%8),
token: 0, amount: 1, valence: 1, block: 1,
}).collect();
let vocab = ParticleIndex::build(links.iter().copied());
let csr = Arc::new(Csr::build(links.into_iter(), &vocab));
let (_worker, state) = EpochWorker::spawn(Arc::clone(&csr), Arc::new(vocab));
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
loop {
{
let guard = state.read().unwrap();
if let Some(epoch) = guard.as_ref() {
let (topo_ok, pos_ok, max_dev) =
super::verify_against_reference(epoch, &csr);
assert!(topo_ok, "ring-8 topology must match cpu-reference");
assert!(pos_ok, "ring-8 positions must be within ε_X of cpu-reference (dev={max_dev:.4})");
let report = super::run_conformance(&csr, &crate::graph::ParticleIndex::empty(), epoch);
let toml = report.to_toml();
assert!(toml.contains("[r1_conformance]"), "toml missing section header");
assert!(toml.contains("P_RENDER_TOPO"), "toml missing TOPO field");
assert!(toml.contains("P_RENDER_POS"), "toml missing POS field");
break;
}
}
assert!(std::time::Instant::now() < deadline, "EpochWorker timed out");
std::thread::sleep(std::time::Duration::from_millis(10));
}
}
#[test]
fn to_toml_format() {
let report = ConformanceReport {
p_render_topo: true, p_render_pos: true, p_render_fps: true,
p_render_pix: false, p_render_tinf: false,
max_pos_deviation: 0.28, mean_fps: 120.0,
};
let toml = report.to_toml();
assert!(toml.starts_with("[r1_conformance]"));
assert!(toml.contains("P_RENDER_TOPO = 1"));
assert!(toml.contains("P_RENDER_POS = 1"));
assert!(toml.contains("P_RENDER_PIX = 0"));
assert!(toml.contains("P_RENDER_FPS = 1"));
assert!(toml.contains("P_RENDER_TINF = 0"));
}
}