open-standards/wai/wai-rs/tests/jpegai_hyperscale_calibration.rs
Transaction Science f4de2c781c wai: D2c M7b gate 6: the switch; the decode's networks run through the operator set
- jpegai_decode::{decode, decode_with_stages} load the bundle's networks
  (load_networks, under acc64) beside its scale path and run the hyper decoders,
  context stages and synthesis through rec_y, rec_uv, syn_y and syn_uv;
  decode_loaded(data, icci, norm, scale path, networks, observe) decodes with
  both loaded once. The direct networks are reached only from
  jpegai_decode::legacy (Front::Direct, Back::Direct, test-only, legacy_int).
- The loaders no longer panic: Weights::from_json, IntNet::from_json and
  IcciNet::from_json return Result<_, BadMember>; a mistyped member is
  model_mismatch. IntNet keeps only well-formed tensors, so a member §13.6.1 reads
  that is mistyped is missing, as the lowering of the bytes refuses it.
- The neural sink lowers and loads the networks from their eight files' bytes
  through the store of loaded sets (keyed by their listing, at acc64), parses only
  the eICCI net besides, and reports a refusal (model_mismatch, field_range)
  without falling back: a_refused_exact_decode_does_not_fall_back gains a
  non-integer network weight and a mistyped eICCI field.
- The browser crate's decodeJpegAiFull reports a mistyped member.
- Goldens, unchanged (build host, release): jpegai_cross_platform's nine pinned
  hashes, the Synthesis stage d56695726bb6ebed… among them; the decoded RGB
  SHA-256 dbc494a5a670914e… (jpegai_reconstruction_binding with
  WAI_JPEGAI_EXPECT_SHA256, jpegai_e2e_full: max|Δ| = 1 on 4.446%).
- Memory (jpegai_memory, reference kernels): with the scale path and the networks
  loaded beforehand the decode holds 72,752,387 bytes, the direct decode
  175,465,380; the networks take 89,727,913 to load and hold 55,530,900.
- Also green: clippy's disallowed-methods guard and jpegai_decode_symbols, the
  sink's three dispatch tests, wai-web's tests and its wasm32 build; include/wai.h
  unchanged.
- CHANGELOG: the M7b entry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 02:00:19 -04:00

67 lines
4.2 KiB
Rust

//! Byte-exact calibration for the fully-integer `hyper_scale_decoder`
//! (`decode_hyper_scale`) against the REFERENCE DECODER on real test256.bin data:
//! run the real hyper-latent `z` through the net and match the reference scale-index
//! output `i_s` exactly. The per-channel ÷2^n is an arithmetic right shift (floor);
//! the exported ONNX's `Div` truncates, which is ~1-LSB wrong on negatives — this
//! test pins the *reference-decoder* semantics, not the ONNX. No ort/onnx needed.
//!
//! The same network runs through the integer operator set, as the decoder runs it: the
//! plane's scale graph (`jpegai-int/1`, integer-operators §13.6.1) loaded under `acc32`,
//! whose output is `I_s` plus the plane's gains. Both must give the reference's `I_s` at
//! every position (max|Δ| = 0).
//!
//! env: WAI_JPEGAI_HYPERSCALE_IO = hyperscale_io_fixture.json
//! { z:[i64], i_s:[i64], z_shape:[1,C,h,w] } (dumped from the reference decoder)
//! WAI_JPEGAI_HYPERSCALE_WEIGHTS = hyperscale_y_weights.json (int weights)
//! WAI_JPEGAI_NORM = norm.json (optional: the plane's gains; without it the graph is
//! lowered with gains of 0, and its output is `I_s` itself)
#![cfg(all(feature = "neural_int", feature = "legacy_int"))]
use wai::jpegai_synthesis::{decode_hyper_scale, IntNet};
use wai_ops::Tensor;
#[test]
fn hyper_scale_byte_exact_vs_reference() {
let io = match std::env::var("WAI_JPEGAI_HYPERSCALE_IO") {
Ok(p) if std::path::Path::new(&p).exists() => p,
_ => { eprintln!("skip: set WAI_JPEGAI_HYPERSCALE_IO to the z→i_s fixture"); return; }
};
let wj = match std::env::var("WAI_JPEGAI_HYPERSCALE_WEIGHTS") {
Ok(p) if std::path::Path::new(&p).exists() => p,
_ => { eprintln!("skip: set WAI_JPEGAI_HYPERSCALE_WEIGHTS to the weights JSON"); return; }
};
let v: serde_json::Value = serde_json::from_slice(&std::fs::read(&io).unwrap()).unwrap();
let z: Vec<i64> = v["z"].as_array().unwrap().iter().map(|x| x.as_i64().unwrap()).collect();
let i_s: Vec<i64> = v["i_s"].as_array().unwrap().iter().map(|x| x.as_i64().unwrap()).collect();
let zs: Vec<usize> = v["z_shape"].as_array().unwrap().iter().map(|x| x.as_u64().unwrap() as usize).collect();
let (c, h, w) = (zs[1], zs[2], zs[3]);
let net = IntNet::from_json(&serde_json::from_slice(&std::fs::read(&wj).unwrap()).unwrap()).unwrap();
let (mine, co, ho, wo) = decode_hyper_scale(&z, c, h, w, &net);
eprintln!("decode_hyper_scale(real z[{c},{h},{w}]) -> [{co},{ho},{wo}] = {} vals", mine.len());
assert_eq!(mine.len(), i_s.len(), "output size mismatch");
let mism = mine.iter().zip(&i_s).filter(|(a, b)| a != b).count();
if let Some((i, (a, b))) = mine.iter().zip(&i_s).enumerate().find(|(_, (a, b))| a != b) {
eprintln!("first mismatch @ {i}: mine={a} ref={b}");
}
eprintln!("hyper_scale vs reference i_s: {}/{} exact, {} mismatched", mine.len() - mism, mine.len(), mism);
assert_eq!(mism, 0, "hyper_scale must be BYTE-EXACT vs the reference decoder; {mism} mismatches");
// Through the integer operator set: the plane's scale graph, loaded under acc32.
let gains: Vec<i64> = match std::env::var("WAI_JPEGAI_NORM") {
Ok(p) if std::path::Path::new(&p).exists() => {
let n: serde_json::Value = serde_json::from_slice(&std::fs::read(&p).unwrap()).unwrap();
let k = if c == 160 { "gain_y" } else { "gain_uv" };
n[k].as_array().unwrap().iter().map(|x| x.as_i64().unwrap()).collect()
}
_ => vec![0; c],
};
let g = wai::jpegai_ops::load_scale(wai::jpegai_ops::scale_net(&net).unwrap(), c, &gains).expect("the plane's scale graph loads under acc32");
let v = wai::int_ops::run1(&g, Tensor::new(c as u32, h as u32, w as u32, z.clone()), &[]).expect("z is in the graph's input range");
let plane = v.data.len() / c;
let ops: Vec<i64> = v.data.iter().enumerate().map(|(i, &x)| x - gains[i / plane]).collect();
let max = ops.iter().zip(&i_s).map(|(a, b)| (a - b).abs()).max().unwrap_or(0);
eprintln!("scale graph (wai-ops, acc32) vs reference i_s: {}/{} exact, max|Δ| = {max}", ops.iter().zip(&i_s).filter(|(a, b)| a == b).count(), i_s.len());
assert_eq!(max, 0, "the scale graph must be BYTE-EXACT vs the reference decoder");
}