All checks were successful
joule-moq-remote host agent / joule-moq-remote · does this change touch it (pull_request) Successful in 7s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 13s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 8s
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 8s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 15s
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 7s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 7s
wai cross-architecture byte-equality / does this change touch it (pull_request) Successful in 13s
WAI operation catalogue / does this change touch it (pull_request) Successful in 7s
WAI energy receipts / does this change touch it (pull_request) Successful in 8s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 6s
WAI conformance / does this change touch it (pull_request) Successful in 13s
wai formal verification (integer core) / does this change touch it (pull_request) Successful in 5s
wai-fleet service / does this change touch it (pull_request) Successful in 8s
wai-key-proxy / does this change touch it (pull_request) Successful in 6s
WAI JPEG-AI whole decode (pinned inputs, required) / does this change touch it (pull_request) Successful in 12s
wai-ledger service / does this change touch it (pull_request) Successful in 7s
WAI neural sink gates / does this change touch it (pull_request) Successful in 5s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 7s
wai-quantum for JavaScript / does this change touch it (pull_request) Successful in 6s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 7s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 15s
wai-sink-android native sink library / does this change touch it (pull_request) Successful in 6s
wai-sink-swift native sink package / does this change touch it (pull_request) Successful in 7s
WAI Studio / does this change touch it (pull_request) Successful in 5s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 5s
WAI mbt2018_mean integer decode (regenerated vectors) / does this change touch it (pull_request) Successful in 13s
wai-web wasm32 / does this change touch it (pull_request) Successful in 7s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Successful in 3m44s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Successful in 36s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 36s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 1m43s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 1m56s
wai formal verification (integer core) / kani (isqrt512) (pull_request) Successful in 27m8s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m39s
wai formal verification (integer core) / every harness in one shard (pull_request) Successful in 12s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 1m10s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 4m2s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Successful in 5m45s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (pull_request) Successful in 7m11s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 8m18s
wai formal verification (integer core) / kani (stage) (pull_request) Successful in 3m36s
wai-key-proxy / proxy · linux (pull_request) Successful in 1m10s
wai-ledger service / service · linux (pull_request) Successful in 2m4s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m48s
wai-fleet service / service · linux (pull_request) Successful in 5m19s
wai formal verification (integer core) / kani (integer-core) (pull_request) Successful in 7m0s
wai formal verification (integer core) / kani (rans64-t2) (pull_request) Successful in 10m40s
WAI neural sink gates / the exact JPEG-AI decoder reports its path (pinned inputs, required) (pull_request) Successful in 2m12s
wai-pack / wai-pack · linux (pull_request) Successful in 1m58s
wai formal verification (integer core) / kani (arith) (pull_request) Successful in 10m50s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 5m53s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 59s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 15m28s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 3m58s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 3m23s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 3m1s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 17m13s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 6m21s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m39s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 9m36s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m35s
wai formal verification (integer core) / kani (plan) (pull_request) Successful in 18m17s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 5m55s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 5m37s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m4s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 10m5s
wai formal verification (integer core) / kani (stage-long) (pull_request) Successful in 20m6s
wai formal verification (integer core) / kani (sound) (pull_request) Successful in 28m7s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 59s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Successful in 1m15s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 3m50s
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Successful in 49s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 6m16s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 7m47s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 7m26s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 2m1s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 25s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 34s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 13m23s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 22m1s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 13s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 30s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 8m13s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m58s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 13m35s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 37s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 2m10s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m14s
wai-fleet service / service · macos (pull_request) Successful in 46s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 19m28s
wai-ledger service / service · macos (pull_request) Successful in 1m8s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 22s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 17s
wai-pack / wai-pack · macos (pull_request) Successful in 2m28s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m46s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 12m58s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m29s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 7m30s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 14m5s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 4m57s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 15m46s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 10m40s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 11m35s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 10m40s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h30m4s
The change of wai/det-source-fixes (152aab5c7..9c4159958, 15 commits), on the fix branch: its changelog entries as files of wai/changes/, its wai-conformance.yml edits as step files, corpus files and test tags, and every other file as the branch has it. - a5bb23634 wai-rs: the inverse GDN's integer roots take no floating point - b9463b4d0 wai-rs: the splat ranges and JSON form are read in integer arithmetic - b20be5196 wai-rs: int_entropy's scale conversions are formed in integers - 437669081 wai-rs: a test that the integer decode paths hold no floating point - 36aade4f1 wai: the integer decode paths without floating point, in the changelog and integer-payloads §10 - f781bf091 wai-rs: the AVX-512 root's comment states the bound s + bit keeps - 799ab8476 wai-rs: float_free reads the other integer entropy decoders too - b79447e64 wai-rs: splat ranges one last digit apart decode to adjacent values - 43f75cc36 wai: the changelog names the decoders float_free reads and the last-digit pairs - 2e308fd28 wai-rs: the parameter sets' JSON is read by the strict reader - 42f848583 wai-rs: int_payload re-exports Binary32 - d1dbe4936 wai-rs: float_free reads the operator set, every integer module, and serde_json's parser - 931699331 wai: the parameter sets' JSON read strictly, in integer-payloads §9 and §14 and the changelog - 97d133380 wai-rs: float_free names what a decode reaches beyond what it reads - 9c4159958 wai: the changelog names the general JSON reader once Co-Authored-By: Claude Opus 5.5 <[email protected]>
603 lines
36 KiB
Rust
603 lines
36 KiB
Rust
// wai-conformance: int-kernels int-kernels-amd
|
||
//! `wai.neural.int_hyper` and the keyframe of `wai.video.int_hyper` through the integer
|
||
//! operator set: their lowering `whm1/1` (`extensions/integer-operators.md` §13.2; `h_s`,
|
||
//! then `g_s` with the RGB8 step of §13.0) and the certificate of every committed set
|
||
//! (§8.5), as this crate loads them (`int_ops`, `int_hyper_synth::ops`).
|
||
//!
|
||
//! The lowered graphs and certificates are committed under `ops-conformance/certificates/`,
|
||
//! where `wai-verify` and the independent checker (`tools/wai_ops_check.py`) each
|
||
//! reproduce them byte for byte (`wai-ops.yml`). Here the lowering this crate runs, from
|
||
//! the models it parses (`int_ops::whm1_net`), must write those same bytes, and the load a
|
||
//! sink makes must give the verdict the certificate records.
|
||
|
||
use std::path::PathBuf;
|
||
|
||
use serde_json::Value;
|
||
use wai::int_hyper_synth::{self as hyper, IntModel};
|
||
use wai_ops::Profile;
|
||
|
||
const CAPS: [&str; 2] = ["wai.neural.int_hyper", "wai.video.int_hyper"];
|
||
|
||
fn wai() -> PathBuf {
|
||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
|
||
}
|
||
|
||
fn read(rel: &str) -> Vec<u8> {
|
||
std::fs::read(wai().join(rel)).unwrap_or_else(|e| panic!("{rel}: {e}"))
|
||
}
|
||
|
||
fn json(rel: &str) -> Value {
|
||
serde_json::from_slice(&read(rel)).unwrap_or_else(|e| panic!("{rel}: {e}"))
|
||
}
|
||
|
||
fn sha256(b: &[u8]) -> String {
|
||
wai_ops::sha256::sha256(b).iter().map(|x| format!("{x:02x}")).collect()
|
||
}
|
||
|
||
/// A committed set of one of the two capabilities, as `ops-conformance/certificates/index.json`
|
||
/// lists it: its name, capability, members (name, path below `wai/`), digest and lowered
|
||
/// graphs (`h_s`, then `g_s`).
|
||
struct Indexed {
|
||
name: String,
|
||
cap: String,
|
||
members: Vec<(String, String)>,
|
||
digest: String,
|
||
roles: [String; 2],
|
||
}
|
||
|
||
fn indexed() -> Vec<Indexed> {
|
||
json("ops-conformance/certificates/index.json")["sets"]
|
||
.as_array()
|
||
.unwrap()
|
||
.iter()
|
||
.filter(|s| CAPS.contains(&s["capability"].as_str().unwrap()))
|
||
.map(|s| {
|
||
assert_eq!(s["lowering"], "whm1/1");
|
||
let roles = s["roles"].as_array().unwrap();
|
||
assert_eq!(roles.len(), 2, "whm1/1 lowers a conditional codec's set to two graphs, h_s and g_s");
|
||
let members = s["members"].as_object().unwrap().iter().map(|(k, v)| (k.clone(), v.as_str().unwrap().to_string())).collect();
|
||
Indexed {
|
||
name: s["name"].as_str().unwrap().to_string(),
|
||
cap: s["capability"].as_str().unwrap().to_string(),
|
||
members,
|
||
digest: s["set_sha256"].as_str().unwrap().to_string(),
|
||
roles: [roles[0].as_str().unwrap().to_string(), roles[1].as_str().unwrap().to_string()],
|
||
}
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
impl Indexed {
|
||
fn member(&self, name: &str) -> Vec<u8> {
|
||
read(&self.members.iter().find(|(k, _)| k == name).unwrap_or_else(|| panic!("{}: no {name}", self.name)).1)
|
||
}
|
||
}
|
||
|
||
/// The two graphs this crate lowers from the models it parses (`int_hyper_synth::ops::lower`),
|
||
/// whose conversion (`int_ops::whm1_net`) must give the networks `wai-ops` parses from the
|
||
/// bytes.
|
||
fn lowered_from_the_parsed_models(g_s: &[u8], h_s: &[u8]) -> (wai_ops::Graph, wai_ops::Graph) {
|
||
let (g, h) = (IntModel::from_bytes(g_s).unwrap(), IntModel::from_bytes(h_s).unwrap());
|
||
for (m, b) in [(&g, g_s), (&h, h_s)] {
|
||
let net = wai::int_ops::whm1_net(m).expect("a model parsed from bytes converts");
|
||
assert!(net == wai_ops::lower::parse_whm1(b).unwrap(), "the converted model is the one wai-ops parses from the bytes");
|
||
}
|
||
hyper::ops::lower(&g, &h).expect("the set lowers")
|
||
}
|
||
|
||
/// Gate 1 of the migration: every committed set of `wai.neural.int_hyper` and
|
||
/// `wai.video.int_hyper` lowers to its committed graphs, from its members' bytes
|
||
/// (`lower::int_hyper`) and from the models this crate parses; the certificate beside them
|
||
/// is those graphs'; and loading each graph under the capability's profile gives the
|
||
/// certificate's verdict. The sets carry `check` ranges (§13.2), so they meet `acc64` by
|
||
/// construction and not `acc32`; the first `acc32` failure is `h_s`'s input range, at 53
|
||
/// bits for the conformance set `tiny` (Appendix C's prediction) and 52 for the synthetic
|
||
/// one. The tiny set's JSON models, the form the tools write beside the binary one,
|
||
/// convert to the same networks and lower to the same graphs.
|
||
#[test]
|
||
fn every_committed_set_lowers_to_its_committed_graphs_and_verdict() {
|
||
let sets = indexed();
|
||
let names: Vec<(&str, &str)> = sets.iter().map(|s| (s.cap.as_str(), s.name.as_str())).collect();
|
||
assert_eq!(names, [(CAPS[0], "tiny"), (CAPS[1], "tiny"), (CAPS[0], "hyper")], "the committed int_hyper sets");
|
||
for s in &sets {
|
||
let (name, cap) = (&s.name, &s.cap);
|
||
let (g_s, h_s, ent) = (s.member("g_s.whm"), s.member("h_s.whm"), s.member("entropy.json"));
|
||
let members = vec![("entropy.json", ent.clone()), ("g_s.whm", g_s.clone()), ("h_s.whm", h_s.clone())];
|
||
assert_eq!(wai_ops::generate::sets::set_digest(&members), s.digest, "{name}: the set's digest");
|
||
// The entropy tables are the stage operators' data, not lowered; the set's must load.
|
||
hyper::CondEntropy::read(&ent).unwrap_or_else(|e| panic!("{name}: entropy.json: {e}"));
|
||
let lowered = wai_ops::lower::int_hyper(&h_s, &g_s).unwrap_or_else(|r| panic!("{name}: does not lower: {r:?}"));
|
||
let roles: Vec<&str> = lowered.roles.iter().map(|(r, _)| *r).collect();
|
||
assert_eq!(roles, ["h_s", "g_s"], "{name}");
|
||
let (h, g) = lowered_from_the_parsed_models(&g_s, &h_s);
|
||
let cert = json(&format!("ops-conformance/certificates/{cap}/{}.json", s.digest));
|
||
assert_eq!(cert["set_sha256"].as_str(), Some(s.digest.as_str()), "{name}");
|
||
assert_eq!(cert["capability"].as_str(), Some(cap.as_str()), "{name}");
|
||
assert_eq!(cert["lowering"], "whm1/1", "{name}");
|
||
for (k, (role, ours)) in [("h_s", &h), ("g_s", &g)].into_iter().enumerate() {
|
||
let from_bytes = lowered.get(role).unwrap();
|
||
// The verdict first: the load a sink makes.
|
||
let loaded = wai::int_ops::load(from_bytes).unwrap_or_else(|r| panic!("{name} {role}: refused under acc64: {r:?}"));
|
||
assert!(!loaded.meets(Profile::Acc32), "{name} {role}: a graph with check ranges inside it does not meet acc32");
|
||
// Then the committed bytes: the graph, both ways, and the certificate's record of it.
|
||
let graph = from_bytes.to_bytes();
|
||
assert!(graph == read(&s.roles[k]), "{name}: the lowering of the bytes differs from the committed {}", s.roles[k]);
|
||
assert!(ours.to_bytes() == graph, "{name}: the lowering of the parsed models differs from the committed {role}");
|
||
let c = &cert["graphs"][k];
|
||
assert_eq!(c["name"].as_str(), Some(role), "{name}");
|
||
assert_eq!(c["graph_sha256"].as_str(), Some(sha256(&graph).as_str()), "{name} {role}: the certificate is the graph's");
|
||
// The input's check range is the lowering's X_0 for the network's first layer (§13.2).
|
||
let x0 = &c["inputs"][0];
|
||
let t = &from_bytes.tensors[0];
|
||
assert_eq!((x0["policy"].as_str(), x0["lo"].as_str(), x0["hi"].as_str()), (Some("check"), Some(t.lo.to_string().as_str()), Some(t.hi.to_string().as_str())), "{name} {role}");
|
||
assert!(t.hi > 0 && t.lo == -t.hi, "{name} {role}: a symmetric check range");
|
||
}
|
||
let v = &cert["verdict"];
|
||
assert_eq!((v["acc64"].as_bool(), v["acc32"].as_bool()), (Some(true), Some(false)), "{name}: the certificate's verdict");
|
||
let bits = if name == "tiny" { "53" } else { "52" };
|
||
let first = &v["acc32_first_failure"];
|
||
assert_eq!(
|
||
(first["graph"].as_str(), first["where"].as_str(), first["index"].as_str(), first["key"].as_str(), first["bits"].as_str()),
|
||
(Some("h_s"), Some("input"), Some("0"), Some("range"), Some(bits)),
|
||
"{name}: the first acc32 failure"
|
||
);
|
||
// The decode's own load: both networks, h_s first.
|
||
let (gm, hm) = (IntModel::from_bytes(&g_s).unwrap(), IntModel::from_bytes(&h_s).unwrap());
|
||
let nets = hyper::ops::load(&gm, &hm).unwrap_or_else(|r| panic!("{name}: {r}"));
|
||
assert!(nets.h_s.graph() == lowered.get("h_s").unwrap() && nets.g_s.graph() == lowered.get("g_s").unwrap(), "{name}: the decode loads the committed graphs");
|
||
}
|
||
// The tiny set's JSON models, which the byte-exact keyframe golden and the browser's
|
||
// debug decode read, convert to the networks of its binary models.
|
||
let dir = "wai-rs/tests/vectors/int_hyper";
|
||
let from_json = |f: &str| IntModel::try_from_json(&json(&format!("{dir}/{f}"))).unwrap();
|
||
let (g, h) = hyper::ops::lower(&from_json("tiny_g_s.json"), &from_json("tiny_h_s.json")).unwrap();
|
||
let (gb, hb) = lowered_from_the_parsed_models(&read(&format!("{dir}/tiny_g_s.bin")), &read(&format!("{dir}/tiny_h_s.bin")));
|
||
assert!(g == gb && h == hb, "the tiny set's JSON and binary models lower alike");
|
||
}
|
||
|
||
/// The set of the pretrained model (`mbt2018_mean`, exported to integers) is too large to
|
||
/// commit, so its certificate is committed by the set's digest and checked against the
|
||
/// regenerated set in `wai-sota-vectors.yml`, where `tests/int_hyper_synth.rs` also holds
|
||
/// this crate's lowering of it to the graphs the certificate records. Its verdict is
|
||
/// Appendix C's: `acc64`, first failing `acc32` at `h_s`'s input range, 41 bits.
|
||
#[test]
|
||
fn the_pretrained_sets_certificate_is_the_one_appendix_c_predicts() {
|
||
let ext = json("ops-conformance/certificates/external.json");
|
||
let s = ext["sets"].as_array().unwrap().iter().find(|s| s["capability"] == CAPS[0]).expect("the pretrained set is listed");
|
||
assert_eq!((s["lowering"].as_str(), s["profile"].as_str()), (Some("whm1/1"), Some("acc64")));
|
||
let digest = s["set_sha256"].as_str().unwrap();
|
||
let cert = json(&format!("ops-conformance/certificates/{}/{digest}.json", CAPS[0]));
|
||
let names: Vec<&str> = cert["graphs"].as_array().unwrap().iter().map(|g| g["name"].as_str().unwrap()).collect();
|
||
assert_eq!(names, ["h_s", "g_s"]);
|
||
let v = &cert["verdict"];
|
||
assert_eq!((v["acc64"].as_bool(), v["acc32"].as_bool()), (Some(true), Some(false)));
|
||
let first = &v["acc32_first_failure"];
|
||
assert_eq!(
|
||
(first["graph"].as_str(), first["where"].as_str(), first["key"].as_str(), first["bits"].as_str()),
|
||
(Some("h_s"), Some("input"), Some("range"), Some("41"))
|
||
);
|
||
}
|
||
|
||
/// The typed form of a parsed `WIH1` payload.
|
||
fn stream<'a>(k: &'a wai::int_wire::Wih1<'a>) -> hyper::CondStream<'a> {
|
||
hyper::CondStream {
|
||
m: k.m as usize, sh: k.sh as usize, sw: k.sw as usize, n_z: k.n_z as usize, zh: k.zh as usize, zw: k.zw as usize,
|
||
z_rans: k.z_rans, rans: k.rans,
|
||
}
|
||
}
|
||
|
||
/// A committed set's three members, parsed: `g_s`, `h_s` and the entropy tables.
|
||
fn set_of(v: &Value) -> (IntModel, IntModel, hyper::CondEntropy) {
|
||
let m = |k: &str| read(v["set"][k].as_str().unwrap());
|
||
let ent = hyper::CondEntropy::read(&m("entropy.json")).unwrap();
|
||
(IntModel::from_bytes(&m("g_s.whm")).unwrap(), IntModel::from_bytes(&m("h_s.whm")).unwrap(), ent)
|
||
}
|
||
|
||
fn hex(b: &[u8]) -> String {
|
||
b.iter().map(|x| format!("{x:02x}")).collect()
|
||
}
|
||
|
||
/// Every frame's SHA-256 and the clip's, against the sidecar entry `key` of
|
||
/// `int_video/sha256.json`; every frame's FNV-1a and the clip's, against `conf`.
|
||
fn assert_clip(frames: &[Vec<u8>], key: &str, conf: &Value) {
|
||
use sha2::Digest;
|
||
let side = json("wai-rs/tests/vectors/int_video/sha256.json");
|
||
let (want, fnv) = (side[key]["frames_sha256"].as_array().unwrap(), conf["frame_hashes"].as_array().unwrap());
|
||
assert_eq!((want.len(), fnv.len()), (frames.len(), frames.len()), "{key}: frame count");
|
||
let (mut all, mut all_fnv) = (Vec::new(), Vec::new());
|
||
for (i, f) in frames.iter().enumerate() {
|
||
let d = sha2::Sha256::digest(f);
|
||
assert_eq!(hex(&d), want[i].as_str().unwrap(), "{key}: frame {i}");
|
||
let h = format!("{:016x}", wai::int_video::fnv1a(f));
|
||
assert_eq!(h, fnv[i].as_str().unwrap(), "{key}: frame {i}'s FNV-1a");
|
||
all.extend_from_slice(&d);
|
||
all_fnv.extend((0..8).map(|j| u8::from_str_radix(&h[2 * j..2 * j + 2], 16).unwrap()));
|
||
}
|
||
assert_eq!(hex(&sha2::Sha256::digest(&all)), side[key]["clip_sha256"].as_str().unwrap(), "{key}: the clip's SHA-256");
|
||
assert_eq!(format!("{:016x}", wai::int_video::fnv1a(&all_fnv)), conf["clip_hash"].as_str().unwrap(), "{key}: the clip's FNV-1a");
|
||
}
|
||
|
||
/// Gate 2 of the migration: golden equality. Every committed vector of
|
||
/// `wai.neural.int_hyper` and `wai.video.int_hyper` (`int-payload-conformance/vectors.json`)
|
||
/// decodes through the lowering (`int_hyper_synth::ops`) to the image the direct decode
|
||
/// gives (`int_hyper_synth::legacy`), and to every digest the corpus commits for it:
|
||
/// - `tiny` (`WIH1`): the SHA-256 sidecar and the export's FNV-1a, from the payload, from
|
||
/// the JSON form it is converted from, and with the set's JSON models, the form the
|
||
/// byte-exact keyframe golden reads;
|
||
/// - `video_nk` (`WIV1`, keyframe 1): its nested `WIH1` keyframe, then the clip's integer
|
||
/// P-frames on it, every frame's and the clip's SHA-256 and FNV-1a.
|
||
#[test]
|
||
fn every_committed_vector_decodes_through_the_lowering_to_its_golden() {
|
||
let index = json("int-payload-conformance/vectors.json");
|
||
let vs: Vec<&Value> = index["vectors"].as_array().unwrap().iter().filter(|v| CAPS.contains(&v["capability"].as_str().unwrap())).collect();
|
||
let names: Vec<&str> = vs.iter().map(|v| v["name"].as_str().unwrap()).collect();
|
||
assert_eq!(names, ["tiny", "video_nk"], "the committed int_hyper vectors");
|
||
let dir = "wai-rs/tests/vectors";
|
||
// tiny: WIH1, its JSON form, and the JSON models.
|
||
let v = vs[0];
|
||
let (g_s, h_s, ent) = set_of(v);
|
||
let p = read(v["payload"].as_str().unwrap());
|
||
assert_eq!(sha256(&p), v["payload_sha256"].as_str().unwrap());
|
||
let k = wai::int_wire::parse_wih1(&p).unwrap();
|
||
let bs = hyper::CondBitstream::try_from_value(&json(v["converted_from"]["json"].as_str().unwrap())).unwrap();
|
||
let from_json = |f: &str| IntModel::try_from_json(&json(&format!("{dir}/int_hyper/{f}"))).unwrap();
|
||
let (gj, hj) = (from_json("tiny_g_s.json"), from_json("tiny_h_s.json"));
|
||
let want_sha = json(v["sidecar"].as_str().unwrap())["tiny"]["sha256"].as_str().unwrap().to_string();
|
||
let conf = json(&format!("{dir}/int_hyper/tiny.json"));
|
||
for (form, g, h, st) in [("WIH1", &g_s, &h_s, stream(&k)), ("JSON", &g_s, &h_s, bs.stream()), ("JSON models", &gj, &hj, stream(&k))] {
|
||
let ops = hyper::ops::decode(g, h, &ent, &st).unwrap_or_else(|r| panic!("tiny {form}: {r}"));
|
||
let direct = hyper::legacy::decode_conditional_checked(g, h, &ent, &st).unwrap_or_else(|r| panic!("tiny {form}: {r}"));
|
||
assert!(ops == direct, "tiny {form}: the decode through the lowering differs from the direct decode");
|
||
assert_eq!((ops.1, ops.2), (16, 16), "tiny {form}");
|
||
assert_eq!(sha256(&ops.0), want_sha, "tiny {form}: the SHA-256 sidecar");
|
||
assert_eq!(format!("{:016x}", hyper::fnv1a(&ops.0)), conf["rgb_hash"].as_str().unwrap(), "tiny {form}: the export's FNV-1a");
|
||
}
|
||
// video_nk: WIV1 with keyframe 1.
|
||
let v = vs[1];
|
||
let (g_s, h_s, ent) = set_of(v);
|
||
let p = read(v["payload"].as_str().unwrap());
|
||
assert_eq!(sha256(&p), v["payload_sha256"].as_str().unwrap());
|
||
let w = wai::int_wire::parse_wiv1(&p, Some(1)).unwrap();
|
||
let k = wai::int_wire::parse_wih1(w.wih1.expect("keyframe 1 nests a WIH1")).unwrap();
|
||
let (frame0, h, wd) = hyper::ops::decode(&g_s, &h_s, &ent, &stream(&k)).unwrap();
|
||
assert!(hyper::legacy::decode_conditional_checked(&g_s, &h_s, &ent, &stream(&k)).unwrap() == (frame0.clone(), h, wd), "video_nk: the keyframe differs from the direct decode's");
|
||
assert_eq!((h, wd), (w.h as usize, w.w as usize), "video_nk: the keyframe is the clip's size");
|
||
let s = wai::int_video::VideoStream {
|
||
n_frames: w.n_frames as usize, h: w.h as usize, w: w.w as usize, block: w.block as usize,
|
||
q: w.q as i64, offset: w.offset, mv_precision: w.mv_precision, cdf: &w.cdf, mvs: &w.mvs, rans: w.rans,
|
||
};
|
||
let clip = wai::int_video::decode_pframes_typed(&frame0, &s).unwrap();
|
||
assert_clip(&clip.frames, "video_nk", &json(&format!("{dir}/int_video/video_nk.json")));
|
||
}
|
||
|
||
/// The code integer-payloads §8 gives a refusal of the conditional decode.
|
||
fn code_of(r: hyper::Refused) -> &'static str {
|
||
wai::int_payload::IntPayloadError::from(r).code()
|
||
}
|
||
|
||
/// The committed conformance set and payload (`int-payload-conformance`'s `tiny`).
|
||
const DIR: &str = "wai-rs/tests/vectors/int_hyper";
|
||
|
||
/// A typed `WIH1` payload that owns its streams, so a fixture can change any field.
|
||
#[derive(Clone)]
|
||
struct Owned {
|
||
m: usize,
|
||
sh: usize,
|
||
sw: usize,
|
||
n_z: usize,
|
||
zh: usize,
|
||
zw: usize,
|
||
z_rans: Vec<u8>,
|
||
rans: Vec<u8>,
|
||
}
|
||
|
||
impl Owned {
|
||
fn committed() -> Owned {
|
||
let p = read(&format!("{DIR}/tiny_bitstream.wih1"));
|
||
let k = wai::int_wire::parse_wih1(&p).unwrap();
|
||
Owned {
|
||
m: k.m as usize, sh: k.sh as usize, sw: k.sw as usize, n_z: k.n_z as usize, zh: k.zh as usize, zw: k.zw as usize,
|
||
z_rans: k.z_rans.to_vec(), rans: k.rans.to_vec(),
|
||
}
|
||
}
|
||
fn st(&self) -> hyper::CondStream<'_> {
|
||
hyper::CondStream { m: self.m, sh: self.sh, sw: self.sw, n_z: self.n_z, zh: self.zh, zw: self.zw, z_rans: &self.z_rans, rans: &self.rans }
|
||
}
|
||
}
|
||
|
||
/// The committed set, parsed, changed by `f`: `(g_s, h_s, entropy tables)`.
|
||
fn tiny(f: impl FnOnce(&mut IntModel, &mut IntModel, &mut hyper::CondEntropy)) -> (IntModel, IntModel, hyper::CondEntropy) {
|
||
let mut g = IntModel::from_bytes(&read(&format!("{DIR}/tiny_g_s.bin"))).unwrap();
|
||
let mut h = IntModel::from_bytes(&read(&format!("{DIR}/tiny_h_s.bin"))).unwrap();
|
||
let mut e = hyper::CondEntropy::read(&read(&format!("{DIR}/tiny_entropy.json"))).unwrap();
|
||
f(&mut g, &mut h, &mut e);
|
||
(g, h, e)
|
||
}
|
||
|
||
/// The committed payload, changed by `f`.
|
||
fn payload(f: impl FnOnce(&mut Owned)) -> Owned {
|
||
let mut o = Owned::committed();
|
||
f(&mut o);
|
||
o
|
||
}
|
||
|
||
/// Both decodes of a typed payload against a set: through the lowering, then directly.
|
||
fn both(set: &(IntModel, IntModel, hyper::CondEntropy), o: &Owned) -> (Result<Vec<u8>, &'static str>, Result<Vec<u8>, &'static str>) {
|
||
let (g, h, e) = set;
|
||
(
|
||
hyper::ops::decode(g, h, e, &o.st()).map(|d| d.0).map_err(code_of),
|
||
hyper::legacy::decode_conditional_checked(g, h, e, &o.st()).map(|d| d.0).map_err(code_of),
|
||
)
|
||
}
|
||
|
||
/// Gate 3 of the migration: refusal parity, on the committed fixtures. Each fixture of
|
||
/// `int-payload-conformance` for `wai.neural.int_hyper` and `wai.video.int_hyper` is
|
||
/// refused by a sink with its code. For one that parses (`decode`), its `WIH1` (the
|
||
/// payload, or a `WIV1`'s keyframe) decodes through the lowering and directly to the same
|
||
/// image, or is refused by both with the same code: the fixture's, where the keyframe is
|
||
/// what it refuses.
|
||
#[test]
|
||
fn every_committed_refusal_fixture_keeps_its_code() {
|
||
use std::collections::BTreeMap;
|
||
let (mut n, mut keyframes) = (0, 0);
|
||
for f in json("int-payload-conformance/vectors.json")["refusals"].as_array().unwrap().iter().filter(|f| CAPS.contains(&f["capability"].as_str().unwrap())) {
|
||
let (name, cap, want) = (f["name"].as_str().unwrap(), f["capability"].as_str().unwrap(), f["code"].as_str().unwrap());
|
||
let p = read(f["payload"].as_str().unwrap());
|
||
let set: BTreeMap<String, Vec<u8>> = f["set"].as_object().unwrap().iter().map(|(k, v)| (k.clone(), read(v.as_str().unwrap()))).collect();
|
||
let view: BTreeMap<String, &[u8]> = set.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect();
|
||
let params = wai::int_payload::resolve(cap, None, &[wai::pin::HeldBytes::Set { listing: b"", files: &view }], None).expect("an unpinned set resolves");
|
||
let sink = wai::int_payload::decode_int(cap, &p, ¶ms).map(|_| ()).map_err(|e| e.code());
|
||
assert_eq!(sink, Err(want), "{name}: the sink's code");
|
||
if f["layer"] == "decode" {
|
||
let nested = if cap == CAPS[0] { p.clone() } else { wai::int_wire::parse_wiv1(&p, Some(1)).unwrap().wih1.unwrap().to_vec() };
|
||
let k = wai::int_wire::parse_wih1(&nested).unwrap_or_else(|e| panic!("{name}: a decode fixture's keyframe parses: {e:?}"));
|
||
let g = IntModel::from_bytes(&set["g_s.whm"]).unwrap();
|
||
let h = IntModel::from_bytes(&set["h_s.whm"]).unwrap();
|
||
let e = hyper::CondEntropy::read(&set["entropy.json"]).unwrap();
|
||
let ops = hyper::ops::decode(&g, &h, &e, &stream(&k)).map(|d| d.0).map_err(code_of);
|
||
let direct = hyper::legacy::decode_conditional_checked(&g, &h, &e, &stream(&k)).map(|d| d.0).map_err(code_of);
|
||
assert!(ops == direct, "{name}: the two decodes of the keyframe differ: {:?} / {:?}", ops.as_ref().err(), direct.as_ref().err());
|
||
if cap == CAPS[0] {
|
||
assert_eq!(ops, Err(want), "{name}: the WIH1 fixture's code, both ways");
|
||
}
|
||
keyframes += 1;
|
||
}
|
||
n += 1;
|
||
}
|
||
assert!(n >= 33 && keyframes >= 15, "the committed int_hyper fixtures: {n}, {keyframes} decoded");
|
||
}
|
||
|
||
/// Gate 3, beside the committed fixtures: a fixture for each refusal the direct decode
|
||
/// makes (integer-payloads §7, in its order: the set against the payload, the networks
|
||
/// planned, the shapes, then each stream and each network as it runs), each made from the
|
||
/// committed set and payload by one change. Each is refused by both decodes with the same
|
||
/// code, the one integer-payloads §8 gives that rule. The committed set and payload decode.
|
||
#[test]
|
||
fn every_refusal_of_the_direct_decode_is_refused_alike_through_the_lowering() {
|
||
use hyper::IntLayer;
|
||
let base = tiny(|_, _, _| {});
|
||
let (ops, direct) = both(&base, &Owned::committed());
|
||
assert!(ops.is_ok() && ops == direct, "the fixtures' base decodes alike");
|
||
let same = || tiny(|_, _, _| {});
|
||
let as_is = || payload(|_| {});
|
||
// A hyper-latent stream that runs out: two symbols of frequency 1 in 65536 each cost
|
||
// 16 bits, so the 8 symbols need more than the stream's 64-bit start, cut short.
|
||
let costly_z = |e: &mut hyper::CondEntropy| {
|
||
for k in 0..2 {
|
||
e.z_cdfs[k] = vec![0, 1, 2, 1 << 16];
|
||
e.z_cdf_lengths[k] = 4;
|
||
}
|
||
};
|
||
let costly_z_stream = || {
|
||
let mut enc = wai::rans::RansEncoder::new();
|
||
for _ in 0..8 {
|
||
assert!(enc.encode(-8, &[0, 1, 2, 1 << 16], 4, -8));
|
||
}
|
||
let mut s = enc.flush();
|
||
s.truncate(s.len() - 4);
|
||
s
|
||
};
|
||
let h_bias = |f: fn(&mut Vec<i64>, usize)| tiny(move |_, h, _| {
|
||
if let IntLayer::Conv { bias, .. } = &mut h.layers[2] { f(bias, 4) }
|
||
});
|
||
let fixtures: Vec<(&str, &str, (IntModel, IntModel, hyper::CondEntropy), Owned)> = vec![
|
||
("h_s's afrac not g_s's", "model_mismatch", tiny(|_, h, _| h.afrac = 11), as_is()),
|
||
("the set's afrac of 0", "model_mismatch", tiny(|g, h, _| (g.afrac, h.afrac) = (0, 0)), as_is()),
|
||
("the set's afrac of 31", "model_mismatch", tiny(|g, h, _| (g.afrac, h.afrac) = (31, 31)), as_is()),
|
||
("no scale edge", "model_mismatch", tiny(|_, _, e| { e.edges.clear(); e.cdfs.clear(); e.cdf_lengths.clear(); e.offsets.clear(); }), as_is()),
|
||
("a latent table short", "model_mismatch", tiny(|_, _, e| { e.cdfs.pop(); }), as_is()),
|
||
("a hyper-latent median short", "model_mismatch", tiny(|_, _, e| { e.z_median_q.pop(); }), as_is()),
|
||
("a table that does not end at 2^16", "model_mismatch", tiny(|_, _, e| { let n = e.cdf_lengths[1]; e.cdfs[1][n - 1] = 65535; }), as_is()),
|
||
("a hyper-latent table's length past its row", "model_mismatch", tiny(|_, _, e| e.z_cdf_lengths[0] = e.z_cdfs[0].len() + 1), as_is()),
|
||
("a latent dimension of 0", "field_range", same(), payload(|o| o.m = 0)),
|
||
("more hyper-latent channels than tables", "model_mismatch", same(), payload(|o| o.n_z = 3)),
|
||
("a hyper-latent past 2^26", "too_large", same(), payload(|o| (o.zh, o.zw) = (1 << 13, 1 << 13))),
|
||
("a latent past 2^26", "too_large", same(), payload(|o| (o.sh, o.sw) = (1 << 12, 1 << 12))),
|
||
("an uncropped plane of h_s past 2^26", "too_large", same(), payload(|o| (o.zh, o.zw) = (1 << 11, 1 << 11))),
|
||
("an h_s layer's bias short", "model_mismatch", h_bias(|b, _| { b.pop(); }), as_is()),
|
||
("padding that leaves g_s no output", "model_mismatch", tiny(|g, _, _| if let IntLayer::ConvT { pad, .. } = &mut g.layers[2] { *pad = 1000 }), as_is()),
|
||
("an h_s output that is not the declared latent's shape", "geometry_mismatch", same(), payload(|o| o.sh = 5)),
|
||
("a g_s that gives four channels", "model_mismatch", tiny(|g, _, _| if let IntLayer::ConvT { out_ch, weight, mult, bias, .. } = &mut g.layers[2] {
|
||
*out_ch = 4;
|
||
*weight = vec![1; 4 * 4 * 9];
|
||
mult.push(mult[0]);
|
||
bias.push(0);
|
||
}), as_is()),
|
||
("a hyper-latent stream too short to be one", "field_range", same(), payload(|o| o.z_rans.truncate(4))),
|
||
("a hyper-latent stream that runs out", "stream_exhausted", tiny(|_, _, e| costly_z(e)), payload(|o| o.z_rans = costly_z_stream())),
|
||
("a word left unread in the hyper-latent stream", "stream_noncanonical", same(), payload(|o| o.z_rans.extend([0, 0, 0, 0]))),
|
||
("a hyper-latent median at i64's end", "field_range", tiny(|_, _, e| e.z_median_q[0] = i64::MAX), as_is()),
|
||
("hyper-latents past h_s's range", "field_range", tiny(|_, _, e| e.z_median_q = vec![1 << 60; 2]), as_is()),
|
||
("a latent stream too short to be one", "field_range", same(), payload(|o| o.rans.truncate(4))),
|
||
("a latent stream that runs out", "stream_exhausted", same(), payload(|o| { let n = o.rans.len() / 2; o.rans.truncate(n - n % 4); })),
|
||
("a word left unread in the latent stream", "stream_noncanonical", same(), payload(|o| o.rans.extend([0, 0, 0, 0]))),
|
||
("means past g_s's range", "field_range", h_bias(|b, m| b[m..].iter_mut().for_each(|v| *v = 1 << 55)), as_is()),
|
||
("means near i64's end", "field_range", h_bias(|b, m| b[m..].iter_mut().for_each(|v| *v = i64::MAX - (1 << 20))), as_is()),
|
||
];
|
||
for (what, want, set, o) in &fixtures {
|
||
let (ops, direct) = both(set, o);
|
||
assert_eq!((ops.err(), direct.err()), (Some(*want), Some(*want)), "{what}: through the lowering, then directly");
|
||
}
|
||
}
|
||
|
||
/// Gate 3, at the edge of every `check` range: for each layer of the committed `h_s` and
|
||
/// `g_s`, a network of that layer alone (with the set's header; a leaky ReLU followed by a
|
||
/// zero 1×1 transposed convolution, since a network with no layer of tag 0, 1 or 2 does
|
||
/// not lower) is lowered, and its input's `check` range is `[−X_ℓ, X_ℓ]`, the range the
|
||
/// full graph gives tensor `ℓ` (§13.2). An input whose largest magnitude is `X_ℓ` runs,
|
||
/// through the lowering and directly, to the same output; one at `X_ℓ + 1` is refused by
|
||
/// both: by the graph (`field_range`, at input 0) and by the direct decode's check of that
|
||
/// layer (`run_net_checked`, a value out of range at layer 0). So the ranges refuse exactly
|
||
/// what integer-payloads §7 *Exactness* refuses, at the same layer.
|
||
#[test]
|
||
fn every_check_range_refuses_exactly_what_the_direct_decode_refuses() {
|
||
use hyper::{legacy::run_net_checked, IntLayer};
|
||
use wai_ops::{Code, Site, Tensor};
|
||
let mut seen = 0;
|
||
for (net, rgb8) in [("tiny_h_s.bin", false), ("tiny_g_s.bin", true)] {
|
||
let set = read(&format!("{DIR}/{net}"));
|
||
let full = wai_ops::lower::whm1(&set, rgb8).unwrap();
|
||
let n = wai_ops::lower::parse_whm1(&set).unwrap().layers.len();
|
||
for l in 0..n {
|
||
let mut one = IntModel::from_bytes(&set).unwrap();
|
||
let layer = one.layers.swap_remove(l);
|
||
let leaky = matches!(layer, IntLayer::Leaky { .. });
|
||
one.layers = vec![layer];
|
||
let c = full.tensors[l].channels;
|
||
if leaky {
|
||
one.layers.push(IntLayer::ConvT { out_ch: 1, k: 1, stride: 1, pad: 0, opad: 0, weight: vec![0; c as usize], mult: vec![1], bias: vec![0] });
|
||
}
|
||
let g = wai_ops::lower::whm1_net(&wai::int_ops::whm1_net(&one).unwrap(), false).unwrap();
|
||
let (lo, hi) = (g.tensors[0].lo, g.tensors[0].hi);
|
||
assert_eq!((g.tensors[0].channels, lo, hi), (c, full.tensors[l].lo, full.tensors[l].hi), "{net} layer {l}: the range of tensor {l}");
|
||
assert!(hi > 0 && lo == -hi, "{net} layer {l}: a symmetric check range [{lo}, {hi}]");
|
||
let loaded = wai::int_ops::load(&g).unwrap();
|
||
let x = |v: i64| {
|
||
let mut d = vec![0i64; c as usize * 4];
|
||
d[c as usize * 2 + 1] = v;
|
||
d
|
||
};
|
||
for v in [hi, -hi] {
|
||
let ops = wai::int_ops::run1(&loaded, Tensor::new(c, 2, 2, x(v)), &[]).unwrap_or_else(|r| panic!("{net} layer {l}: {v} runs: {r:?}"));
|
||
let (direct, ..) = run_net_checked(&one, &x(v), c as usize, 2, 2).unwrap_or_else(|r| panic!("{net} layer {l}: {v} runs directly: {r}"));
|
||
assert!(ops.data == direct, "{net} layer {l}: at {v} the two differ");
|
||
}
|
||
for v in [hi + 1, -hi - 1] {
|
||
let r = wai::int_ops::run1(&loaded, Tensor::new(c, 2, 2, x(v)), &[]).expect_err("one past the range");
|
||
assert_eq!((r.code, r.at), (Code::FieldRange, Site::Input(0)), "{net} layer {l}: {v}");
|
||
let d = run_net_checked(&one, &x(v), c as usize, 2, 2).expect_err("one past the range, directly");
|
||
assert!(d.out_of_range && d.layer == 0, "{net} layer {l}: {v}: {d}");
|
||
}
|
||
seen += 1;
|
||
}
|
||
}
|
||
assert_eq!(seen, 6, "every layer of the committed h_s and g_s");
|
||
}
|
||
|
||
/// Gate 5 of the migration, the whole-decoder half: the decode through the lowering under
|
||
/// the reference kernels (the interpreter computes every operator) and under each fast
|
||
/// policy (the convolutions, the transposed convolutions and the inverse GDN on this
|
||
/// crate's fast kernels, serial and threaded) gives the same image, and the same
|
||
/// refusals, on the committed vector (as `WIH1`, as its JSON form, and with the set's JSON
|
||
/// models), and on sets whose hyper-latent channel 0 lies about the edge of `h_s`'s input
|
||
/// check range (its median at `±X_0` and one past it). The per-operator half is `int_ops`' equivalence
|
||
/// tests, one of which counts the fast kernels' runs on this set.
|
||
#[test]
|
||
fn every_kernel_policy_decodes_through_the_lowering_alike() {
|
||
use wai::int_kernels::{with_policy, Policy};
|
||
static POLICIES: std::sync::LazyLock<Vec<Policy>> = std::sync::LazyLock::new(wai::int_kernels::equivalence_policies);
|
||
let x0 = wai_ops::lower::whm1(&read(&format!("{DIR}/tiny_h_s.bin")), false).unwrap().tensors[0].hi;
|
||
let base = Owned::committed();
|
||
let bs = hyper::CondBitstream::try_from_value(&json(&format!("{DIR}/tiny_bitstream.json"))).unwrap();
|
||
let from_json = |f: &str| IntModel::try_from_json(&json(&format!("{DIR}/{f}"))).unwrap();
|
||
let json_models = (from_json("tiny_g_s.json"), from_json("tiny_h_s.json"), tiny(|_, _, _| {}).2);
|
||
let json_form = Owned { m: bs.m, sh: bs.sh, sw: bs.sw, n_z: bs.n_z, zh: bs.zh, zw: bs.zw, z_rans: bs.z_rans.clone(), rans: bs.rans.clone() };
|
||
let mut cases: Vec<(String, (IntModel, IntModel, hyper::CondEntropy), Owned)> =
|
||
vec![("WIH1".into(), tiny(|_, _, _| {}), base.clone()), ("JSON".into(), tiny(|_, _, _| {}), json_form), ("JSON models".into(), json_models, base.clone())];
|
||
// Channel 0's median moved to ±X_0 or one past it: its elements, the median plus each
|
||
// symbol at 2^12, lie about the edge of h_s's input check range.
|
||
for (what, t) in [("channel 0 at X_0", x0), ("at X_0 + 1", x0 + 1), ("at -X_0", -x0), ("at -X_0 - 1", -x0 - 1)] {
|
||
cases.push((what.into(), tiny(move |_, _, e| e.z_median_q[0] = t), base.clone()));
|
||
}
|
||
for (k, (what, set, o)) in cases.iter().enumerate() {
|
||
let (g, h, e) = set;
|
||
let want = with_policy(POLICIES[0], || hyper::ops::decode(g, h, e, &o.st())).map(|d| d.0).map_err(code_of);
|
||
assert!(k >= 3 || want.is_ok(), "{what}: the committed form decodes");
|
||
for pol in &POLICIES[1..] {
|
||
let got = with_policy(*pol, || hyper::ops::decode(g, h, e, &o.st())).map(|d| d.0).map_err(code_of);
|
||
assert!(got == want, "{what}: {pol:?} differs from the interpreter alone");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Every committed `WIH1` payload, alone or inside another file (a `WIV1`'s keyframe, an
|
||
/// envelope, a staged base, a counting fixture): each occurrence of the magic in the
|
||
/// corpora that hold integer payloads whose bytes from there parse as a `WIH1` of exactly
|
||
/// the length its header and stream lengths give. Returns `(where, payload)` pairs.
|
||
fn committed_wih1_payloads() -> Vec<(String, Vec<u8>)> {
|
||
fn files(d: &std::path::Path, out: &mut Vec<PathBuf>) {
|
||
let mut es: Vec<_> = std::fs::read_dir(d).unwrap().filter_map(|e| e.ok()).map(|e| e.path()).collect();
|
||
es.sort();
|
||
for p in es {
|
||
if p.is_dir() { files(&p, out) } else { out.push(p) }
|
||
}
|
||
}
|
||
let mut all = Vec::new();
|
||
for d in ["int-payload-conformance", "staged-conformance", "staged-measure-conformance", "http-conformance/objects", "wai-rs/tests/vectors"] {
|
||
files(&wai().join(d), &mut all);
|
||
}
|
||
let u32_at = |b: &[u8], i: usize| b.get(i..i + 4).map(|s| u32::from_le_bytes(s.try_into().unwrap()) as usize);
|
||
let mut v = Vec::new();
|
||
for f in all {
|
||
let b = std::fs::read(&f).unwrap();
|
||
for i in (0..b.len().saturating_sub(3)).filter(|&i| &b[i..i + 4] == b"WIH1") {
|
||
let Some(lz) = u32_at(&b, i + 32) else { continue };
|
||
let Some(ly) = i.checked_add(36 + lz).and_then(|j| u32_at(&b, j)) else { continue };
|
||
let end = i + 40 + lz + ly;
|
||
if let Some(p) = b.get(i..end).filter(|p| wai::int_wire::parse_wih1(p).is_ok()) {
|
||
v.push((format!("{}@{i}", f.strip_prefix(wai()).unwrap().display()), p.to_vec()));
|
||
}
|
||
}
|
||
}
|
||
v
|
||
}
|
||
|
||
/// The scan for the load rules the switch brings (integer-operators §15.3 item 3): no
|
||
/// committed set or payload is affected. Every committed set of either capability has a
|
||
/// layer of tag 0, 1 or 2 in each network, header shifts under 128, no bias of magnitude
|
||
/// `2^63 − 1` or more, and its two networks lower and load under `acc64`; every committed
|
||
/// `WIH1` payload, wherever it is carried, decodes through the lowering to the bytes the
|
||
/// direct decode gives, or is refused by both with the same code, against the set every
|
||
/// committed one is decoded with (`tiny`). (The other files that hold the magic are code:
|
||
/// the browser crate's recorded build, its demo page and its scripts.)
|
||
#[test]
|
||
fn the_new_load_rules_affect_no_committed_set_or_payload() {
|
||
use wai_ops::lower::NetLayer as N;
|
||
for s in indexed() {
|
||
for (role, rgb8) in [("h_s.whm", false), ("g_s.whm", true)] {
|
||
let b = s.member(role);
|
||
let net = wai_ops::lower::parse_whm1(&b).unwrap();
|
||
assert!(net.layers.iter().any(|l| matches!(l, N::ConvT { .. } | N::Conv { .. } | N::Igdn { .. })), "{} {role}: no layer of tag 0, 1 or 2", s.name);
|
||
assert!(net.shift < 128 && net.gf < 128, "{} {role}: a header shift of 128 or more", s.name);
|
||
for l in &net.layers {
|
||
if let N::ConvT { bias, .. } | N::Conv { bias, .. } = l {
|
||
assert!(bias.iter().all(|b| b.unsigned_abs() < i64::MAX as u64), "{} {role}: a bias of magnitude 2^63 - 1 or more", s.name);
|
||
}
|
||
}
|
||
let g = wai_ops::lower::whm1(&b, rgb8).unwrap_or_else(|r| panic!("{} {role}: does not lower: {r:?}", s.name));
|
||
assert!(wai::int_ops::load(&g).is_ok(), "{} {role}: does not load under acc64", s.name);
|
||
}
|
||
}
|
||
let set = tiny(|_, _, _| {});
|
||
let (mut decoded, mut refused) = (0, 0);
|
||
let payloads = committed_wih1_payloads();
|
||
for (at, p) in &payloads {
|
||
let k = wai::int_wire::parse_wih1(p).unwrap();
|
||
let o = Owned { m: k.m as usize, sh: k.sh as usize, sw: k.sw as usize, n_z: k.n_z as usize, zh: k.zh as usize, zw: k.zw as usize, z_rans: k.z_rans.to_vec(), rans: k.rans.to_vec() };
|
||
let (ops, direct) = both(&set, &o);
|
||
assert!(ops == direct, "{at}: the decode through the lowering differs from the direct decode");
|
||
if ops.is_ok() { decoded += 1 } else { refused += 1 }
|
||
}
|
||
let staged = payloads.iter().filter(|(at, _)| at.starts_with("staged-conformance")).count();
|
||
assert!(decoded >= 10 && refused >= 5 && staged >= 5, "the committed WIH1 payloads, at least those of this revision: {decoded} decoded, {refused} refused, {staged} staged");
|
||
}
|