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]>
441 lines
24 KiB
Rust
441 lines
24 KiB
Rust
// wai-conformance: int-kernels int-kernels-amd
|
||
//! `wai.splat.int_codec` through the integer operator set: its lowering `whm1/1`
|
||
//! (`extensions/integer-operators.md` §13.2, with the RGB8 step of §13.0) and the
|
||
//! certificate of every committed set (§8.5), as this crate loads them (`int_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 model it parses (`int_ops::whm1_net`), must write those same bytes, and the load a
|
||
//! sink makes must give the verdict the certificate records.
|
||
//!
|
||
//! The decode through the lowering (`int_splat::ops`) must give, on every committed
|
||
//! vector, the bytes of the direct decode (`int_splat::legacy`) and the vector's
|
||
//! committed digests.
|
||
|
||
use std::path::PathBuf;
|
||
|
||
use serde_json::Value;
|
||
use wai::int_hyper_synth::IntModel;
|
||
use wai::int_splat::{self, SplatJson, SplatStream};
|
||
use wai_ops::Profile;
|
||
|
||
const CAP: &str = "wai.splat.int_codec";
|
||
|
||
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()
|
||
}
|
||
|
||
/// The `wai.splat.int_codec` sets `ops-conformance/certificates/index.json` lists:
|
||
/// `(name, set file, set digest, lowered graph file)`, each relative to `wai/`.
|
||
fn indexed() -> Vec<(String, String, String, String)> {
|
||
json("ops-conformance/certificates/index.json")["sets"]
|
||
.as_array()
|
||
.unwrap()
|
||
.iter()
|
||
.filter(|s| s["capability"] == CAP)
|
||
.map(|s| {
|
||
assert_eq!(s["lowering"], "whm1/1");
|
||
let roles = s["roles"].as_array().unwrap();
|
||
assert_eq!(roles.len(), 1, "whm1/1 lowers a splat set to one graph, g_s");
|
||
(
|
||
s["name"].as_str().unwrap().to_string(),
|
||
s["members"]["g_s.whm"].as_str().unwrap().to_string(),
|
||
s["set_sha256"].as_str().unwrap().to_string(),
|
||
roles[0].as_str().unwrap().to_string(),
|
||
)
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
/// The graph this crate lowers from the model it parses: `int_ops::whm1_net`, then
|
||
/// `whm1/1` with the RGB8 step.
|
||
fn lowered_from_the_parsed_model(set: &[u8]) -> wai_ops::Graph {
|
||
let m = IntModel::from_bytes(set).expect("the WHM1 set parses");
|
||
let net = wai::int_ops::whm1_net(&m).expect("a model parsed from bytes converts");
|
||
assert!(net == wai_ops::lower::parse_whm1(set).unwrap(), "the converted model is the one wai-ops parses from the bytes");
|
||
wai_ops::lower::whm1_net(&net, true).expect("the set lowers")
|
||
}
|
||
|
||
/// Gate 1 of the migration: every committed `wai.splat.int_codec` set lowers to its
|
||
/// committed graph, from its bytes (`lower::splat`) and from the model this crate parses;
|
||
/// the certificate beside it is that graph's; and loading the graph under the
|
||
/// capability's profile gives the certificate's verdict. The set carries `check` ranges
|
||
/// (§13.2), so it meets `acc64` by construction and not `acc32`; its first `acc32` failure
|
||
/// is `g_s`'s input range, at 44 bits.
|
||
#[test]
|
||
fn every_committed_set_lowers_to_its_committed_graph_and_verdict() {
|
||
let sets = indexed();
|
||
let names: Vec<&str> = sets.iter().map(|s| s.0.as_str()).collect();
|
||
assert_eq!(names, ["splat"], "the committed splat sets");
|
||
for (name, set, digest, role) in &sets {
|
||
let bytes = read(set);
|
||
let lowered = wai_ops::lower::splat(&bytes).unwrap_or_else(|r| panic!("{name}: does not lower: {r:?}"));
|
||
let roles: Vec<&str> = lowered.roles.iter().map(|(r, _)| *r).collect();
|
||
assert_eq!(roles, ["g_s"], "{name}");
|
||
let g = lowered.get("g_s").unwrap();
|
||
// The verdict first: the load a sink makes.
|
||
let loaded = wai::int_ops::load(g).unwrap_or_else(|r| panic!("{name}: refused under acc64: {r:?}"));
|
||
assert!(!loaded.meets(Profile::Acc32), "{name}: a set with check ranges inside its graph does not meet acc32");
|
||
// Then the committed bytes: the set, its lowered graph (both ways) and its certificate.
|
||
assert_eq!(&sha256(&bytes), digest, "{name}: the set's digest");
|
||
let graph = g.to_bytes();
|
||
assert!(graph == read(role), "{name}: the lowering of the bytes differs from the committed {role}");
|
||
assert!(lowered_from_the_parsed_model(&bytes).to_bytes() == graph, "{name}: the lowering of the parsed model differs from the committed {role}");
|
||
let cert = json(&format!("ops-conformance/certificates/{CAP}/{digest}.json"));
|
||
assert_eq!(cert["set_sha256"].as_str(), Some(digest.as_str()), "{name}");
|
||
assert_eq!(cert["lowering"], "whm1/1", "{name}");
|
||
assert_eq!(cert["graphs"][0]["name"], "g_s", "{name}");
|
||
assert_eq!(cert["graphs"][0]["graph_sha256"].as_str(), Some(sha256(&graph).as_str()), "{name}: the certificate is the graph's");
|
||
let v = &cert["verdict"];
|
||
assert_eq!((v["acc64"].as_bool(), v["acc32"].as_bool()), (Some(true), Some(false)), "{name}: the certificate's verdict");
|
||
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("g_s"), Some("input"), Some("0"), Some("range"), Some("44")),
|
||
"{name}: the first acc32 failure Appendix C predicts"
|
||
);
|
||
// The input's check range is the lowering's X_0 for the first layer (§13.2).
|
||
let x0 = &cert["graphs"][0]["inputs"][0];
|
||
assert_eq!((x0["policy"].as_str(), x0["lo"].as_str(), x0["hi"].as_str()), (Some("check"), Some("-5632249656115"), Some("5632249656115")), "{name}");
|
||
assert_eq!(g.tensors[0].lo, -5632249656115, "{name}");
|
||
assert_eq!(g.tensors[0].hi, 5632249656115, "{name}");
|
||
}
|
||
}
|
||
|
||
/// The typed form of a parsed `WIS1` payload.
|
||
fn stream<'a>(v: &'a wai::int_wire::Wis1<'a>) -> SplatStream<'a> {
|
||
SplatStream {
|
||
afrac: v.afrac as u32, c: v.c as usize, sh: v.sh as usize, sw: v.sw as usize, s: v.s as usize,
|
||
n_attr: v.attr as usize, cdfs: &v.cdfs, offsets: &v.offsets, median_q: &v.median_q, rans: v.rans,
|
||
}
|
||
}
|
||
|
||
/// The `wai.splat.int_codec` vectors `int-payload-conformance/vectors.json` indexes:
|
||
/// `(name, payload, set member g_s.whm, JSON form, sidecar)`, each relative to `wai/`.
|
||
fn vectors() -> Vec<(String, String, String, String, String)> {
|
||
json("int-payload-conformance/vectors.json")["vectors"]
|
||
.as_array()
|
||
.unwrap()
|
||
.iter()
|
||
.filter(|v| v["capability"] == CAP)
|
||
.map(|v| {
|
||
let s = |k: &Value| k.as_str().unwrap().to_string();
|
||
(s(&v["name"]), s(&v["payload"]), s(&v["set"]["g_s.whm"]), s(&v["converted_from"]["json"]), s(&v["sidecar"]))
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
/// Gate 2 of the migration: golden equality. Every committed `wai.splat.int_codec`
|
||
/// vector decodes through the lowering (`int_splat::ops`) to the bytes the direct decode
|
||
/// gives (`int_splat::legacy`), and to every digest the corpus commits for it: the
|
||
/// SHA-256 sidecar and the export's FNV-1a. The payload (`WIS1`) and the JSON form it is
|
||
/// converted from both decode, and alike. The staged corpus's splat base carries the
|
||
/// same payload, so it is covered too.
|
||
#[test]
|
||
fn every_committed_vector_decodes_through_the_lowering_to_its_golden() {
|
||
let vs = vectors();
|
||
assert_eq!(vs.len(), 1, "the committed splat vectors");
|
||
for (name, payload, set, from, sidecar) in &vs {
|
||
let gs = IntModel::from_bytes(&read(set)).unwrap();
|
||
let p = read(payload);
|
||
let v = wai::int_wire::parse_wis1(&p).unwrap_or_else(|e| panic!("{name}: {e:?}"));
|
||
let bs = json(from);
|
||
let j = SplatJson::read(&read(from)).unwrap();
|
||
let want = json(sidecar)[name]["sha256"].as_str().unwrap().to_string();
|
||
for (form, st) in [("WIS1", stream(&v)), ("JSON", j.stream())] {
|
||
let ops = int_splat::ops::decode_typed(&gs, &st).unwrap_or_else(|r| panic!("{name} {form}: {r}"));
|
||
let direct = int_splat::legacy::decode_typed(&gs, &st).unwrap_or_else(|r| panic!("{name} {form}: {r}"));
|
||
assert!(ops.attr == direct.attr, "{name} {form}: the decode through the lowering differs from the direct decode");
|
||
assert_eq!((ops.n_attr, ops.s), (direct.n_attr, direct.s), "{name} {form}");
|
||
assert_eq!(sha256(&ops.attr), want, "{name} {form}: the SHA-256 sidecar");
|
||
assert_eq!(Some(format!("{:016x}", ops.attr_hash()).as_str()), bs["attr_hash"].as_str(), "{name} {form}: the export's FNV-1a");
|
||
}
|
||
let base = read("staged-conformance/cases/splat_attr_1l/base.wai");
|
||
assert!(base.windows(p.len()).any(|w| w == p.as_slice()), "{name}: the staged splat base carries this payload");
|
||
}
|
||
}
|
||
|
||
/// The committed vector's set and payload (`int-payload-conformance`'s `splat`).
|
||
const SET: &str = "wai-rs/tests/vectors/int_splat/splat_gs.bin";
|
||
const PAYLOAD: &str = "wai-rs/tests/vectors/int_splat/splat.wis1";
|
||
|
||
/// A typed cloud that owns its tables, so a fixture can change any field.
|
||
#[derive(Clone)]
|
||
struct Owned {
|
||
afrac: u32,
|
||
c: usize,
|
||
sh: usize,
|
||
sw: usize,
|
||
s: usize,
|
||
n_attr: usize,
|
||
cdfs: Vec<Vec<i32>>,
|
||
offsets: Vec<i32>,
|
||
median_q: Vec<i64>,
|
||
rans: Vec<u8>,
|
||
}
|
||
|
||
impl Owned {
|
||
fn committed() -> Owned {
|
||
let p = read(PAYLOAD);
|
||
let v = wai::int_wire::parse_wis1(&p).unwrap();
|
||
Owned {
|
||
afrac: v.afrac as u32, c: v.c as usize, sh: v.sh as usize, sw: v.sw as usize, s: v.s as usize, n_attr: v.attr as usize,
|
||
cdfs: v.cdfs.clone(), offsets: v.offsets.clone(), median_q: v.median_q.clone(), rans: v.rans.to_vec(),
|
||
}
|
||
}
|
||
fn st(&self) -> SplatStream<'_> {
|
||
SplatStream {
|
||
afrac: self.afrac, c: self.c, sh: self.sh, sw: self.sw, s: self.s, n_attr: self.n_attr,
|
||
cdfs: &self.cdfs, offsets: &self.offsets, median_q: &self.median_q, rans: &self.rans,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// The committed set, parsed, changed by `f`.
|
||
fn model(f: impl FnOnce(&mut IntModel)) -> IntModel {
|
||
let mut m = IntModel::from_bytes(&read(SET)).unwrap();
|
||
f(&mut m);
|
||
m
|
||
}
|
||
|
||
/// The committed cloud, changed by `f`.
|
||
fn cloud(f: impl FnOnce(&mut Owned)) -> Owned {
|
||
let mut o = Owned::committed();
|
||
f(&mut o);
|
||
o
|
||
}
|
||
|
||
/// The code integer-payloads §8 gives a refusal of the splat decode.
|
||
fn code_of(r: wai::int_hyper_synth::Refused) -> &'static str {
|
||
wai::int_payload::IntPayloadError::from(r).code()
|
||
}
|
||
|
||
/// Gate 3 of the migration: refusal parity, on the committed fixtures. Each
|
||
/// `wai.splat.int_codec` fixture of `int-payload-conformance` is refused by a sink with
|
||
/// its code; one that parses (`decode`) is refused by the decode through the lowering and
|
||
/// by the direct decode alike, with that code.
|
||
#[test]
|
||
fn every_committed_refusal_fixture_keeps_its_code() {
|
||
let mut n = 0;
|
||
for f in json("int-payload-conformance/vectors.json")["refusals"].as_array().unwrap().iter().filter(|f| f["capability"] == CAP) {
|
||
let (name, want) = (f["name"].as_str().unwrap(), f["code"].as_str().unwrap());
|
||
let (p, set) = (read(f["payload"].as_str().unwrap()), read(f["set"]["g_s.whm"].as_str().unwrap()));
|
||
let params = wai::int_payload::resolve(CAP, None, &[wai::pin::HeldBytes::File(&set)], 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 gs = IntModel::from_bytes(&set).unwrap();
|
||
let v = wai::int_wire::parse_wis1(&p).unwrap_or_else(|e| panic!("{name}: a decode fixture parses: {e:?}"));
|
||
let ops = int_splat::ops::decode_typed(&gs, &stream(&v)).err().map(code_of);
|
||
let direct = int_splat::legacy::decode_typed(&gs, &stream(&v)).err().map(code_of);
|
||
assert_eq!((ops, direct), (Some(want), Some(want)), "{name}: through the lowering, then directly");
|
||
}
|
||
n += 1;
|
||
}
|
||
assert!(n >= 11, "the committed splat fixtures: {n}");
|
||
}
|
||
|
||
/// Gate 3, beside the committed fixtures: a fixture for each refusal the direct decode
|
||
/// makes (integer-payloads §7, in its order), each made from the committed set and cloud
|
||
/// by one change. Each is refused by both decodes with the same code; the code it states
|
||
/// is integer-payloads §8's for that rule. The committed set and cloud decode.
|
||
#[test]
|
||
fn every_refusal_of_the_direct_decode_is_refused_alike_through_the_lowering() {
|
||
let (gs, base) = (model(|_| {}), Owned::committed());
|
||
assert!(int_splat::ops::decode_typed(&gs, &base.st()).is_ok(), "the fixtures' base decodes");
|
||
let same = || model(|_| {});
|
||
let fixtures: Vec<(&str, &str, IntModel, Owned)> = vec![
|
||
("the set's afrac of 0", "model_mismatch", model(|m| m.afrac = 0), cloud(|_| {})),
|
||
("the set's afrac of 31", "model_mismatch", model(|m| m.afrac = 31), cloud(|_| {})),
|
||
("a cloud afrac not the set's", "model_mismatch", same(), cloud(|o| o.afrac = 11)),
|
||
("no latent channels", "field_range", same(), cloud(|o| o.c = 0)),
|
||
("a median short", "field_range", same(), cloud(|o| { o.median_q.pop(); })),
|
||
("an offset short", "field_range", same(), cloud(|o| { o.offsets.pop(); })),
|
||
("a table short", "field_range", same(), cloud(|o| { o.cdfs.pop(); })),
|
||
("a median past 2^47", "field_range", same(), cloud(|o| o.median_q[3] = (1 << 47) + 1)),
|
||
("a table that does not end at 2^16", "cdf_malformed", same(), cloud(|o| *o.cdfs[2].last_mut().unwrap() = 65535)),
|
||
("a latent past 2^26", "too_large", same(), cloud(|o| (o.sh, o.sw) = (1 << 13, 1 << 13))),
|
||
("an attribute tensor past 2^26", "too_large", same(), cloud(|o| o.s = 1 << 14)),
|
||
("an uncropped plane past 2^26", "too_large", same(), cloud(|o| (o.sh, o.sw) = (1100, 1100))),
|
||
("a layer's bias short", "model_mismatch", model(|m| {
|
||
if let wai::int_hyper_synth::IntLayer::ConvT { bias, .. } = &mut m.layers[0] { bias.pop(); }
|
||
}), cloud(|_| {})),
|
||
("padding that leaves no output", "model_mismatch", model(|m| {
|
||
if let wai::int_hyper_synth::IntLayer::ConvT { pad, .. } = &mut m.layers[2] { *pad = 1000; }
|
||
}), cloud(|_| {})),
|
||
("an attribute side not g_s's", "geometry_mismatch", same(), cloud(|o| o.s = 31)),
|
||
("a stream too short to be one", "field_range", same(), cloud(|o| o.rans.truncate(4))),
|
||
("a stream that runs out", "stream_exhausted", same(), cloud(|o| { let n = o.rans.len() / 2; o.rans.truncate(n - n % 4); })),
|
||
("a word left unread", "stream_noncanonical", same(), cloud(|o| o.rans.extend([0, 0, 0, 0]))),
|
||
("every latent past layer 0's range", "field_range", same(), cloud(|o| o.median_q.iter_mut().for_each(|m| *m = 1 << 47))),
|
||
("every latent below it", "field_range", same(), cloud(|o| o.median_q.iter_mut().for_each(|m| *m = -(1 << 47)))),
|
||
];
|
||
for (what, want, gs, o) in &fixtures {
|
||
let ops = int_splat::ops::decode_typed(gs, &o.st()).err().map(code_of);
|
||
let direct = int_splat::legacy::decode_typed(gs, &o.st()).err().map(code_of);
|
||
assert_eq!((ops, direct), (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 set, a
|
||
/// network of that layer alone (with the set's header) 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 wai::int_hyper_synth::legacy::run_net_checked;
|
||
use wai_ops::{Code, Site, Tensor};
|
||
let set = read(SET);
|
||
let full = wai_ops::lower::whm1(&set, true).unwrap();
|
||
let net = wai_ops::lower::parse_whm1(&set).unwrap();
|
||
for l in 0..net.layers.len() {
|
||
let one = model(|m| {
|
||
let layer = m.layers.swap_remove(l);
|
||
m.layers = vec![layer];
|
||
});
|
||
let g = wai_ops::lower::whm1_net(&wai::int_ops::whm1_net(&one).unwrap(), false).unwrap();
|
||
let (c, lo, hi) = (g.tensors[0].channels, g.tensors[0].lo, g.tensors[0].hi);
|
||
assert_eq!((c, lo, hi), (full.tensors[l].channels, full.tensors[l].lo, full.tensors[l].hi), "layer {l}: the range of tensor {l}");
|
||
assert!(hi > 0 && lo == -hi, "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!("layer {l}: {v} runs: {r:?}"));
|
||
let (direct, ..) = run_net_checked(&one, &x(v), c as usize, 2, 2).unwrap_or_else(|r| panic!("layer {l}: {v} runs directly: {r}"));
|
||
assert!(ops.data == direct, "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)), "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, "layer {l}: {v}: {d}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 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 transposed convolutions on this crate's fast kernels, serial and
|
||
/// threaded) gives the same attribute bytes, and the same refusals, on every committed
|
||
/// vector, as `WIS1` and as its JSON form, and on clouds at the edge of the input's check
|
||
/// range. The per-operator half is `int_ops`' equivalence tests, one of which counts the
|
||
/// fast kernel's 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 gs = model(|_| {});
|
||
let x0 = wai_ops::lower::whm1(&read(SET), true).unwrap().tensors[0].hi;
|
||
let at = |t: i64| cloud(move |o| o.median_q[1] = t - ((o.offsets[1] as i64) << o.afrac));
|
||
let mut clouds: Vec<(String, Owned)> = vec![("WIS1".into(), Owned::committed())];
|
||
let j = SplatJson::read(&read("wai-rs/tests/vectors/int_splat/splat.json")).unwrap();
|
||
clouds.push(("JSON".into(), Owned {
|
||
afrac: j.afrac, c: j.c, sh: j.sh, sw: j.sw, s: j.s, n_attr: j.n_attr,
|
||
cdfs: j.cdfs.clone(), offsets: j.offsets.clone(), median_q: j.median_q.clone(), rans: j.rans.clone(),
|
||
}));
|
||
for (what, t) in [("channel 1's median at X_0", x0), ("at X_0 + 1", x0 + 1), ("at -X_0", -x0), ("at -X_0 - 1", -x0 - 1)] {
|
||
clouds.push((what.into(), at(t)));
|
||
}
|
||
for (k, (what, o)) in clouds.iter().enumerate() {
|
||
let want = with_policy(POLICIES[0], || int_splat::ops::decode_typed(&gs, &o.st())).map(|d| d.attr).map_err(code_of);
|
||
assert!(k >= 2 || want.is_ok(), "{what}: the committed form decodes");
|
||
for pol in &POLICIES[1..] {
|
||
let got = with_policy(*pol, || int_splat::ops::decode_typed(&gs, &o.st())).map(|d| d.attr).map_err(code_of);
|
||
assert!(got == want, "{what}: {pol:?} differs from the interpreter alone");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// The committed `wai.splat.int_codec` sets and payloads, relative to `wai/`: every set
|
||
/// and payload `int-payload-conformance/vectors.json` names for the capability (its
|
||
/// vectors and its refusal fixtures), and every base of a staged case decoded through
|
||
/// the capability, with the set its case names and the payload its envelope carries.
|
||
/// Returns `(set, (where, payload))` pairs.
|
||
fn committed_sets_and_payloads() -> Vec<(String, (String, Vec<u8>))> {
|
||
let mut v = Vec::new();
|
||
let index = json("int-payload-conformance/vectors.json");
|
||
for e in index["vectors"].as_array().unwrap().iter().chain(index["refusals"].as_array().unwrap()) {
|
||
if e["capability"] == CAP {
|
||
let (set, p) = (e["set"]["g_s.whm"].as_str().unwrap(), e["payload"].as_str().unwrap());
|
||
v.push((set.to_string(), (p.to_string(), read(p))));
|
||
}
|
||
}
|
||
let cases = wai().join("staged-conformance/cases");
|
||
let mut dirs: Vec<_> = std::fs::read_dir(&cases).unwrap().filter_map(|e| e.ok()).map(|e| e.path()).collect();
|
||
dirs.sort();
|
||
for d in dirs {
|
||
let Ok(b) = std::fs::read(d.join("case.json")) else { continue };
|
||
let case: Value = serde_json::from_slice(&b).unwrap();
|
||
for base in case["bases"].as_array().into_iter().flatten().filter(|b| b["decoded_via"] == CAP) {
|
||
let f = d.join(base["file"].as_str().unwrap());
|
||
let env = wai::container::Wai::from_bytes(&std::fs::read(&f).unwrap()).unwrap();
|
||
assert_eq!(env.manifest.model_requirement.capability, CAP, "{}", f.display());
|
||
v.push((base["decode"]["set"]["g_s.whm"].as_str().unwrap().to_string(), (f.display().to_string(), env.payload)));
|
||
}
|
||
}
|
||
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 has a layer of tag 0, 1 or
|
||
/// 2, header shifts under 128, no bias of magnitude `2^63 − 1` or more, and lowers and
|
||
/// loads under `acc64`; every committed payload that the direct decode decodes decodes
|
||
/// through the lowering to the same bytes, and every one it refuses is refused with the
|
||
/// same code. (These are the files that carry a `WIS1` payload or name the capability's
|
||
/// set; `WIS1` appears in no other committed payload or envelope.)
|
||
#[test]
|
||
fn the_new_load_rules_affect_no_committed_set_or_payload() {
|
||
use wai_ops::lower::NetLayer as N;
|
||
let all = committed_sets_and_payloads();
|
||
let mut sets: Vec<&str> = all.iter().map(|(s, _)| s.as_str()).collect();
|
||
sets.sort_unstable();
|
||
sets.dedup();
|
||
for set in &sets {
|
||
let bytes = read(set);
|
||
let net = wai_ops::lower::parse_whm1(&bytes).unwrap_or_else(|r| panic!("{set}: {r:?}"));
|
||
assert!(net.layers.iter().any(|l| matches!(l, N::ConvT { .. } | N::Conv { .. } | N::Igdn { .. })), "{set}: no layer of tag 0, 1 or 2");
|
||
assert!(net.shift < 128 && net.gf < 128, "{set}: a header shift of 128 or more");
|
||
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), "{set}: a bias of magnitude 2^63 - 1 or more");
|
||
}
|
||
}
|
||
let g = wai_ops::lower::whm1(&bytes, true).unwrap_or_else(|r| panic!("{set}: does not lower: {r:?}"));
|
||
assert!(wai::int_ops::load(&g).is_ok(), "{set}: does not load under acc64");
|
||
}
|
||
let (mut decoded, mut refused) = (0, 0);
|
||
for (set, (at, p)) in &all {
|
||
let gs = IntModel::from_bytes(&read(set)).unwrap();
|
||
let Ok(v) = wai::int_wire::parse_wis1(p) else {
|
||
refused += 1;
|
||
continue;
|
||
};
|
||
let ops = int_splat::ops::decode_typed(&gs, &stream(&v)).map(|d| d.attr).map_err(code_of);
|
||
let direct = int_splat::legacy::decode_typed(&gs, &stream(&v)).map(|d| d.attr).map_err(code_of);
|
||
assert!(ops == direct, "{at}: the decode through the lowering differs from the direct decode");
|
||
if ops.is_ok() { decoded += 1 } else { refused += 1 }
|
||
}
|
||
assert_eq!(sets.len(), 1, "the committed splat sets");
|
||
assert!(decoded >= 2 && refused >= 11, "the committed splat payloads, at least those of this revision: {decoded} decoded, {refused} refused");
|
||
}
|