Gate 7 made these seven tests require `legacy_int` in Cargo.toml, but their files were still gated on `neural_int` or `neural` alone, which `check-tests-in-ci.py` reports: a file's `#![cfg]` must name what its `[[test]]` requires. Each file is now gated on `all(..., feature = "legacy_int")`, as `jpegai_ops.rs` is. Three test comments named the design's appendix and a plan item. They now name the text's sections (§11's `role_profiles`, §13.6.2's table). Co-Authored-By: Claude Opus 5.5 <[email protected]>
63 lines
2.8 KiB
Rust
63 lines
2.8 KiB
Rust
//! Calibration for [`wai::jpegai_pipeline::reconstruct_yhat`] — the SGMM
|
||
//! autoregressive spine — on REAL `test256.bin` data: rebuild the synthesis-input
|
||
//! latent `y_hat` from the reference's dequantized residual (`resi_dq_y`) and hyper
|
||
//! params (`psi_y`), and compare against the reference `yhat_y`.
|
||
//!
|
||
//! This closes the last structural gap in the decode chain: entropy → dequant →
|
||
//! **reconstruct** → synthesis. The float reference loop reproduces `yhat_y` to
|
||
//! ~139 dB (float round-off); our Q16 integer MCM stages carry the 16-bit PTQ floor
|
||
//! of the MCM nets themselves (~61–74 dB per stage), so we assert well above that.
|
||
//!
|
||
//! env: WAI_JPEGAI_RECON_FIXTURE = recon_fixture.json {resi_dq_y, psi_y, yhat_y, c, h2, w2}
|
||
//! WAI_JPEGAI_MCM_WEIGHTS_DIR = dir with mcm_stage0..3_y.json
|
||
#![cfg(all(feature = "neural_int", feature = "legacy_int"))]
|
||
|
||
use wai::jpegai_pipeline::reconstruct_yhat;
|
||
use wai::jpegai_synthesis::Weights;
|
||
|
||
fn q16(f: &[f64]) -> Vec<i64> { f.iter().map(|&x| (x * 65536.0).round() as i64).collect() }
|
||
|
||
#[test]
|
||
fn reconstruct_yhat_on_real_reference_data() {
|
||
let fx = match std::env::var("WAI_JPEGAI_RECON_FIXTURE") {
|
||
Ok(p) if std::path::Path::new(&p).exists() => p,
|
||
_ => { eprintln!("skip: set WAI_JPEGAI_RECON_FIXTURE"); return; }
|
||
};
|
||
let wdir = match std::env::var("WAI_JPEGAI_MCM_WEIGHTS_DIR") {
|
||
Ok(p) if std::path::Path::new(&p).exists() => p,
|
||
_ => { eprintln!("skip: set WAI_JPEGAI_MCM_WEIGHTS_DIR"); return; }
|
||
};
|
||
let v: serde_json::Value = serde_json::from_slice(&std::fs::read(&fx).unwrap()).unwrap();
|
||
let f64v = |k: &str| v[k].as_array().unwrap().iter().map(|x| x.as_f64().unwrap()).collect::<Vec<f64>>();
|
||
let (c, h2, w2) = (
|
||
v["c"].as_u64().unwrap() as usize,
|
||
v["h2"].as_u64().unwrap() as usize,
|
||
v["w2"].as_u64().unwrap() as usize,
|
||
);
|
||
|
||
let resi = q16(&f64v("resi_dq_y"));
|
||
let psi = q16(&f64v("psi_y"));
|
||
let yhat_ref = f64v("yhat_y");
|
||
|
||
let load = |i: usize| -> Weights {
|
||
let raw = std::fs::read(format!("{wdir}/mcm_stage{i}_y.json")).unwrap();
|
||
Weights::from_json(&serde_json::from_slice(&raw).unwrap()).unwrap()
|
||
};
|
||
let (w0, w1, w2w, w3) = (load(0), load(1), load(2), load(3));
|
||
|
||
let got = reconstruct_yhat(&resi, &psi, c, h2, w2, [&w0, &w1, &w2w, &w3]);
|
||
assert_eq!(got.len(), yhat_ref.len(), "y_hat size mismatch");
|
||
|
||
let (mut maxd, mut se, mut sig) = (0f64, 0f64, 0f64);
|
||
for i in 0..got.len() {
|
||
let g = got[i] as f64 / 65536.0;
|
||
let r = yhat_ref[i];
|
||
let d = (g - r).abs();
|
||
maxd = maxd.max(d);
|
||
se += d * d;
|
||
sig += r * r;
|
||
}
|
||
let snr = 10.0 * (sig / se).log10();
|
||
eprintln!("reconstruct_yhat on real data: max|Δ| = {maxd:.5} SNR = {snr:.2} dB");
|
||
assert!(snr > 45.0, "SGMM reconstruction regressed (SNR {snr:.2} dB)");
|
||
}
|