open-standards/wai/wai-rs/tests/int_synth_ops.rs
Transaction Science 946f3beda9
All checks were successful
joule-moq-remote host agent / joule-moq-remote · does this change touch it (pull_request) Successful in 6s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 7s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 6s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 6s
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 6s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 6s
WAI operation catalogue / does this change touch it (pull_request) Successful in 6s
WAI conformance / does this change touch it (pull_request) Successful in 6s
WAI energy receipts / does this change touch it (pull_request) Successful in 5s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 6s
wai-fleet service / 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 7s
wai-ledger service / does this change touch it (pull_request) Successful in 6s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 1m25s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 6s
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 6s
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 6s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Successful in 1m57s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m7s
WAI Studio / does this change touch it (pull_request) Successful in 6s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 6s
wai-web wasm32 / does this change touch it (pull_request) Successful in 6s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Successful in 29s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 2m1s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Successful in 2m59s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 1m1s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 26s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 4m19s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 16s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m4s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 1m15s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 5m15s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 6m19s
wai-fleet service / service · macos (pull_request) Successful in 1m2s
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 2m46s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 1m43s
wai-ledger service / service · macos (pull_request) Successful in 1m24s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m2s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Successful in 4m18s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 9m19s
wai-pack / wai-pack · macos (pull_request) Successful in 3m8s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 30s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 7m49s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 3m23s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m39s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m9s
wai-fleet service / service · linux (pull_request) Successful in 2m5s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m28s
wai-ledger service / service · linux (pull_request) Successful in 1m43s
wai-pack / wai-pack · linux (pull_request) Successful in 1m55s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 3m0s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m10s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 16m12s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 11m32s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 4m51s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 13m12s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 15m2s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 4m3s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 16m34s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m40s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 17m22s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m44s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m46s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 34s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 17s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 9m28s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 5m7s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 14m49s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m34s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m16s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 11m4s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 13m6s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 5m58s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 10m55s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 9m51s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 10m15s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 9m21s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 30m14s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h4m7s
Changelog entries and the conformance workflow's lists move into files a change adds
Every change to WAI added an entry at the head of wai/CHANGELOG.md, and every new module
appended to the hand-kept lists of .github/workflows/wai-conformance.yml. So any two
branches conflicted on those lines and had to merge one after another. This moves both into
files a change adds.

- Changelog entries: a change adds wai/changes/<yyyy-mm-dd>-<slug>.md holding the entry
  text. scripts/changelog-assemble.py renders the entries into CHANGELOG.md's Unreleased
  section for a release, in a stable order. scripts/check-changelog-fragment.py, run by
  repo-checks on every pull request, fails a pull request that changes wai/ and adds or
  edits no entry file directly in wai/changes/ (a release passes, and so does a commit
  with the trailer line "Changelog: none"). A pull request opened before the check reached
  main passes as it is; one opened after it that adds its entry to the head of Unreleased
  fails and is told where the entry goes. wai breaking reads breaking changes from entry
  files too.
- The conformance workflow: each per-module verifier step is a file in
  .github/wai-conformance/verifiers/, each corpus a file in corpora/, and the
  integer-kernel tests carry a tag line. scripts/wai-conformance-lists.py derives every
  list the workflow kept by hand (WAI_FEATURES, the union --bin and --test lists, the
  features-alone lists, the kernel and corpus steps) from those files when the workflow
  runs. Its `selftest --exact` holds the derived lists to the hand lists of the commit
  before this one, and its dry-run prints every command each job runs: the dry-runs before
  and after this change differ only in the gate's path list, which gains the two paths this
  change adds.
- What stays shared, and how to insert there, is written in wai/CONTRIBUTING.md and
  wai/changes/README.md: wai-rs Cargo.toml features and [[test]] entries, lib.rs, registry
  rows, wai/.gitattributes and the live corpus table take an insert beside the related
  entry, never at the end.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-05 01:41:29 -05:00

385 lines
20 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// wai-conformance: int-kernels int-kernels-amd
//! `wai.neural.int_synth` through the integer operator set: its lowering `int_synth/1`
//! (`extensions/integer-operators.md` §13.1) and the certificate of every committed
//! set (§8.5, §14.3), 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 must
//! write those same bytes, and the load a sink makes must give the verdict the
//! certificate records.
//!
//! The decode through the lowering (`int_synth::decode_from_spec`, the one a sink runs) must give, on
//! every committed vector, the bytes of the direct decode it replaced
//! (`int_synth::legacy`, behind the test-only feature `legacy_int`, which this file
//! needs) and the vector's committed digests. Every refusal of the direct decode is a
//! refusal of the decode through the lowering, with the same code: integer-payloads §13
//! gives one, `payload_malformed`.
use std::path::PathBuf;
use serde_json::Value;
use wai_ops::interp::Tensor;
use wai_ops::{Code, Profile, Site};
const CAP: &str = "wai.neural.int_synth";
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 `int_synth` sets an index of `ops-conformance/certificates/` lists:
/// `(name, set file, set digest, lowered graph file)`, each relative to `wai/`.
fn indexed(index: &str) -> Vec<(String, String, String, String)> {
json(index)["sets"]
.as_array()
.unwrap()
.iter()
.filter(|s| s["capability"] == CAP)
.map(|s| {
assert_eq!(s["lowering"], "int_synth/1");
let roles = s["roles"].as_array().unwrap();
assert_eq!(roles.len(), 1, "int_synth/1 lowers to one graph, synth");
(
s["name"].as_str().unwrap().to_string(),
s["members"]["int_synth.json"].as_str().unwrap().to_string(),
s["set_sha256"].as_str().unwrap().to_string(),
roles[0].as_str().unwrap().to_string(),
)
})
.collect()
}
/// The certificate of the set with digest `d`.
fn certificate(d: &str) -> Value {
json(&format!("ops-conformance/certificates/{CAP}/{d}.json"))
}
/// Gate 1 of the migration: every committed `int_synth` set lowers to its committed
/// graph, the certificate beside it is that graph's, and loading the graph under the
/// capability's profile gives the certificate's verdict. The neural-conformance corpus
/// meets `acc64`; facade, gradient and bloom meet `acc32` too, and photo does not.
#[test]
fn every_committed_set_lowers_to_its_committed_graph_and_verdict() {
let sets = indexed("ops-conformance/certificates/index.json");
let mut names: Vec<&str> = sets.iter().map(|s| s.0.as_str()).collect();
names.sort();
assert_eq!(names, ["bloom", "facade", "gradient", "photo", "synth"], "the committed int_synth sets");
for (name, set, digest, role) in &sets {
let bytes = read(set);
let lowered = wai_ops::lower::int_synth(&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, ["synth"], "{name}");
let g = lowered.get("synth").unwrap();
// The verdict first: the load a sink makes, then the profile each set meets.
let loaded = wai::int_ops::load(g).unwrap_or_else(|r| panic!("{name}: refused under acc64: {r:?}"));
let acc32 = matches!(name.as_str(), "facade" | "gradient" | "bloom" | "synth");
assert_eq!(loaded.meets(Profile::Acc32), acc32, "{name}: whether it meets acc32");
// Then the committed bytes: the set, its lowered graph 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 differs from the committed {role}");
let cert = certificate(digest);
assert_eq!(cert["set_sha256"].as_str(), Some(digest.as_str()), "{name}");
assert_eq!(cert["graphs"][0]["name"], "synth", "{name}");
assert_eq!(cert["graphs"][0]["graph_sha256"].as_str(), Some(sha256(&graph).as_str()), "{name}: the certificate is the graph's");
assert_eq!(cert["verdict"]["acc64"], true, "{name}");
assert_eq!(Value::Bool(loaded.meets(Profile::Acc32)), cert["verdict"]["acc32"], "{name}: the certificate's verdict on acc32");
}
// The browser demo serves byte copies of the corpus's sets.
for name in ["bloom", "facade", "gradient", "photo"] {
let copy = sha256(&read(&format!("wai-web/demo/int_synth/{name}/int_synth.json")));
assert!(sets.iter().any(|s| s.2 == copy), "wai-web/demo/int_synth/{name}: not a committed set");
}
}
/// The two falsification sets of §14.3. `overflow_acc64`'s accumulator passes i64 by
/// one unit, so the capability's load refuses it at load step 8; `claim_acc32`'s
/// passes i32 by one unit, so it loads under `acc64` and does not meet `acc32`.
#[test]
fn the_falsification_sets_fail_the_profile_they_claim() {
let sets = indexed("ops-conformance/certificates/falsification.json");
let mut names: Vec<&str> = sets.iter().map(|s| s.0.as_str()).collect();
names.sort();
assert_eq!(names, ["claim_acc32", "overflow_acc64"]);
for (name, set, digest, role) in &sets {
let bytes = read(set);
assert_eq!(&sha256(&bytes), digest, "{name}");
let lowered = wai_ops::lower::int_synth(&bytes).unwrap();
let g = lowered.get("synth").unwrap();
assert!(g.to_bytes() == read(role), "{name}: the lowering differs from the committed {role}");
let cert = certificate(digest);
match name.as_str() {
"overflow_acc64" => {
assert_eq!(cert["verdict"]["acc64"], false);
let r = wai::int_ops::load(g).expect_err("the overflow set is refused");
assert_eq!((r.code, r.at), (Code::ModelMismatch, Site::Load(8)));
}
_ => {
assert_eq!((cert["verdict"]["acc64"].as_bool(), cert["verdict"]["acc32"].as_bool()), (Some(true), Some(false)));
assert!(!wai::int_ops::load(g).expect("acc64").meets(Profile::Acc32));
}
}
}
}
/// Gate 2 of the migration: golden equality. Every committed `int_synth` vector decodes
/// through the lowering to the bytes the direct decode gives, and to every digest the
/// corpus commits for it: the SHA-256 sidecar, the BLAKE3 `rgb_hash` with its sizes,
/// and photo's reference RGB from the Python integer forward. The browser demo serves
/// byte copies of the corpus's payloads and digests.
#[test]
fn every_committed_vector_decodes_through_the_lowering_to_its_golden() {
let hashes = json("neural-conformance/expected_hashes.json");
let sidecar = json("neural-conformance/sha256.json");
assert!(read("wai-web/demo/int_synth/expected_hashes.json") == read("neural-conformance/expected_hashes.json"));
let mut n = 0;
for (name, want) in hashes.as_object().unwrap() {
let dir = format!("neural-conformance/vectors/{name}");
let (payload, set) = (read(&format!("{dir}/latent.bin")), read(&format!("{dir}/int_synth.json")));
assert!(payload == read(&format!("wai-web/demo/int_synth/{name}/latent.bin")), "{name}: the demo's copy");
let ops = wai::int_synth::decode_from_spec(&payload, &set).unwrap_or_else(|e| panic!("{name}: {e}"));
let direct = wai::int_synth::legacy::decode_from_spec(&payload, &set).unwrap_or_else(|e| panic!("{name}: {e}"));
assert!(ops == direct, "{name}: the decode through the lowering differs from the direct decode");
assert_eq!((ops.width as u64, ops.height as u64), (want["width"].as_u64().unwrap(), want["height"].as_u64().unwrap()), "{name}");
assert_eq!(sha256(&ops.rgb), sidecar[name]["sha256"].as_str().unwrap(), "{name}: the SHA-256 sidecar");
let mut h = blake3::Hasher::new();
h.update(b"wai:int-synth-rgb\x01");
h.update(&ops.rgb);
assert_eq!(h.finalize().to_hex().as_str(), want["rgb_hash"].as_str().unwrap(), "{name}: rgb_hash");
if let Ok(r) = std::fs::read(wai().join(format!("{dir}/ref_rgb.bin"))) {
assert!(ops.rgb == r, "{name}: the Python integer-forward reference");
}
n += 1;
}
assert_eq!(n, 4, "the four committed int_synth vectors");
}
/// A payload of `C` channels at side `2^s_log2` for an `H`×`W` output: the latents are
/// `fill`, and `extra` bytes are added (negative: removed) at the end.
fn payload(h: u32, w: u32, c: u16, s_log2: u8, fill: u8, extra: i64) -> Vec<u8> {
let mut p = Vec::new();
p.extend(h.to_le_bytes());
p.extend(w.to_le_bytes());
p.extend(c.to_le_bytes());
p.extend([s_log2, 0]);
let n = (c as usize) << (2 * s_log2 as usize);
p.resize((12 + n as i64 + extra) as usize, fill);
p
}
/// One layer of `int_synth.json`, every weight `w`.
fn layer(in_ch: u64, out_ch: u64, k: u64, stride: u64, pad: u64, w: i64) -> Value {
serde_json::json!({
"in_ch": in_ch, "out_ch": out_ch, "k": k, "stride": stride, "pad": pad,
"weight": vec![w; (in_ch * out_ch * k * k) as usize], "bias": vec![1; out_ch as usize], "shift": 1, "relu": true,
})
}
fn set(layers: Vec<Value>) -> Vec<u8> {
serde_json::to_vec(&serde_json::json!({ "layers": layers })).unwrap()
}
/// The fixtures: one for each refusal the direct decode makes (integer-payloads §13),
/// each beside a payload and set it decodes. One layer, 1 → 3 channels, k 2, stride 2,
/// on a 1×1 latent: a 2×2 output.
fn refusal_fixtures() -> Vec<(&'static str, Vec<u8>, Vec<u8>)> {
let good_set = set(vec![layer(1, 3, 2, 2, 0, 3)]);
let good = payload(2, 2, 1, 0, 9, 0);
let edit = |f: &dyn Fn(&mut Value)| {
let mut v: Value = serde_json::from_slice(&good_set).unwrap();
f(&mut v);
serde_json::to_vec(&v).unwrap()
};
let p = |what, pay: Vec<u8>| (what, pay, good_set.clone());
let q = |what, s: Vec<u8>| (what, good.clone(), s);
vec![
p("no payload", vec![]),
p("a payload shorter than its header", good[..11].to_vec()),
p("a latent short", payload(2, 2, 1, 0, 9, -1)),
p("a byte past the latents", payload(2, 2, 1, 0, 9, 1)),
p("a side of 2^64", { let mut v = good.clone(); v[10] = 64; v }),
p("a side of 2^255", { let mut v = good.clone(); v[10] = 255; v }),
p("C·S·S past the word", { let mut v = payload(2, 2, 1, 0, 9, 0); v[8..10].copy_from_slice(&u16::MAX.to_le_bytes()); v[10] = 31; v }),
p("an output taller than H", payload(1, 2, 1, 0, 9, 0)),
p("an output narrower than W", payload(2, 3, 1, 0, 9, 0)),
p("H of 0", payload(0, 2, 1, 0, 9, 0)),
p("first in_ch is not C", payload(2, 2, 2, 0, 9, 0)),
q("a set that is not JSON", b"{".to_vec()),
q("a set with no layers member", b"{}".to_vec()),
q("layers that is not a list", br#"{"layers":{}}"#.to_vec()),
q("a layer that is not an object", br#"{"layers":[1]}"#.to_vec()),
q("a layer without k", edit(&|v| { v["layers"][0].as_object_mut().unwrap().remove("k"); })),
q("a negative stride", edit(&|v| v["layers"][0]["stride"] = (-2).into())),
q("a k with a fraction", edit(&|v| v["layers"][0]["k"] = 2.5.into())),
q("a negative shift", edit(&|v| v["layers"][0]["shift"] = (-1).into())),
q("a relu that is not a boolean", edit(&|v| v["layers"][0]["relu"] = 1.into())),
q("a weight short", edit(&|v| { v["layers"][0]["weight"].as_array_mut().unwrap().pop(); })),
q("a bias past out_ch", edit(&|v| v["layers"][0]["bias"].as_array_mut().unwrap().push(0.into()))),
q("a weight past i32", edit(&|v| v["layers"][0]["weight"][0] = (1i64 << 31).into())),
q("a bias below i32", edit(&|v| v["layers"][0]["bias"][0] = (-(1i64 << 31) - 1).into())),
q("layers that do not chain", set(vec![layer(1, 2, 1, 1, 0, 1), layer(3, 3, 2, 2, 0, 1)])),
q("four output channels", set(vec![layer(1, 4, 2, 2, 0, 3)])),
q("an output side of 0", set(vec![layer(1, 3, 2, 2, 1, 3)])),
]
}
/// The code a sink reports for a decode of `wai.neural.int_synth` (`int_payload`, the
/// integer decode path the dispatch takes).
fn code(payload: &[u8], set: &[u8]) -> Result<(), &'static str> {
let params = wai::int_payload::resolve(CAP, None, &[wai::pin::HeldBytes::File(set)], None).expect("an unpinned set resolves");
wai::int_payload::decode_int(CAP, payload, &params).map(|_| ()).map_err(|e| e.code())
}
/// Gate 3 of the migration: refusal parity. Each fixture, refused by the direct decode,
/// is refused by the decode through the lowering, and a sink reports it as
/// `payload_malformed`, the one code integer-payloads §13 gives. Beside them, the
/// payload and set they are edited from decode both ways.
#[test]
fn every_refusal_of_the_direct_decode_is_refused_through_the_lowering() {
let good = (payload(2, 2, 1, 0, 9, 0), set(vec![layer(1, 3, 2, 2, 0, 3)]));
let a = wai::int_synth::decode_from_spec(&good.0, &good.1).expect("the fixtures' base decodes");
assert_eq!(Ok(a.clone()), wai::int_synth::legacy::decode_from_spec(&good.0, &good.1));
assert_eq!(a.rgb, [14u8; 12], "(9·3 + 1) >> 1 at every sample");
assert_eq!(code(&good.0, &good.1), Ok(()));
let fixtures = refusal_fixtures();
for (what, pay, s) in &fixtures {
let direct = wai::int_synth::legacy::decode_from_spec(pay, s);
let ops = wai::int_synth::decode_from_spec(pay, s);
assert!(direct.is_err(), "{what}: the direct decode refuses it: {direct:?}");
assert!(ops.is_err(), "{what}: the decode through the lowering refuses it: {ops:?}");
assert_eq!(code(pay, s), Err("payload_malformed"), "{what}");
}
}
/// The lowered graph's one `check` range, its input's `[−128, 127]` (§13.0's driver
/// contract), at its ends and one past them: a payload's latents are signed bytes, so
/// only a graph-level decode reaches past them, and the graph refuses it (`field_range`,
/// at input 0).
#[test]
fn the_latent_range_refuses_one_past_each_end() {
let lowered = wai_ops::lower::int_synth(&read("neural-conformance/vectors/gradient/int_synth.json")).unwrap();
let g = wai::int_ops::load(lowered.get("synth").unwrap()).unwrap();
let (c, s) = (4u32, 8u32);
let x = |v: i64| {
let mut d = vec![0i64; (c * s * s) as usize];
d[37] = v;
Tensor::new(c, s, s, d)
};
for v in [-128, 127] {
assert!(wai::int_ops::run1(&g, x(v), &[]).is_ok(), "{v} is a latent");
}
for v in [-129, 128] {
let r = wai::int_ops::run1(&g, x(v), &[]).expect_err("one past the latent range");
assert_eq!((r.code, r.at), (Code::FieldRange, Site::Input(0)), "{v}");
}
}
/// 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 bytes on every committed vector, and on the fixtures' base
/// case. The per-operator half is `int_ops`' equivalence tests.
#[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 mut cases: Vec<(String, Vec<u8>, Vec<u8>)> = json("neural-conformance/expected_hashes.json")
.as_object()
.unwrap()
.keys()
.map(|name| {
let dir = format!("neural-conformance/vectors/{name}");
(name.clone(), read(&format!("{dir}/latent.bin")), read(&format!("{dir}/int_synth.json")))
})
.collect();
cases.push(("the fixtures' base".into(), payload(2, 2, 1, 0, 9, 0), set(vec![layer(1, 3, 2, 2, 0, 3)])));
for (name, p, s) in &cases {
let want = with_policy(POLICIES[0], || wai::int_synth::decode_from_spec(p, s)).unwrap_or_else(|e| panic!("{name}: {e}"));
for pol in &POLICIES[1..] {
let got = with_policy(*pol, || wai::int_synth::decode_from_spec(p, s));
assert!(got.as_ref() == Ok(&want), "{name}: {pol:?} differs from the interpreter alone");
}
}
}
/// The committed `int_synth` sets and payloads: every directory under
/// `neural-conformance/vectors/`, the browser demo's `wai-web/demo/int_synth/` and
/// `ops-conformance/sets/` that holds an `int_synth.json`, with its `latent.bin` where
/// it has one. Relative to `wai/`.
fn committed_sets_and_payloads() -> Vec<(String, Option<String>)> {
let mut v = Vec::new();
for root in ["neural-conformance/vectors", "wai-web/demo/int_synth", "ops-conformance/sets"] {
let mut dirs: Vec<String> = std::fs::read_dir(wai().join(root))
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.path().join("int_synth.json").exists())
.map(|e| format!("{root}/{}", e.file_name().to_string_lossy()))
.collect();
dirs.sort();
for d in dirs {
let p = format!("{d}/latent.bin");
let payload = wai().join(&p).exists().then_some(p);
v.push((format!("{d}/int_synth.json"), payload));
}
}
v
}
/// O12: the load rules the switch brings (integer-operators §15.3 item 1, and the two
/// the lowering makes that its text now states) affect no committed set or payload.
/// Every committed set has layers, each with `out_ch`, `k` and `stride` at least 1,
/// fields that fit 32 bits and a shift of at most 63 (so none from 64 to 127 changes
/// meaning either), and lowers and loads under `acc64`; the one exception is
/// `overflow_acc64`, the falsification set made to fail its certificate. Every
/// committed payload has a reserved byte of 0, `C ≥ 1` and `C·S·S ≤ 2^26`, is exactly
/// its length, and decodes through the lowering against its set.
#[test]
fn the_new_load_rules_affect_no_committed_set_or_payload() {
let all = committed_sets_and_payloads();
let (mut sets, mut payloads) = (0, 0);
for (set_path, payload_path) in &all {
let bytes = read(set_path);
let v: Value = serde_json::from_slice(&bytes).unwrap();
let layers = v["layers"].as_array().unwrap();
assert!(!layers.is_empty(), "{set_path}: no layers");
for (i, l) in layers.iter().enumerate() {
let f = |k: &str, d: u64| l.get(k).map_or(Some(d), Value::as_u64).unwrap_or_else(|| panic!("{set_path} layer {i}: {k}"));
for k in ["in_ch", "out_ch", "k", "stride", "pad"] {
assert!(f(k, u64::MAX) <= u32::MAX as u64, "{set_path} layer {i}: {k} past 32 bits");
}
assert!(f("out_pad", 0) <= u32::MAX as u64, "{set_path} layer {i}: out_pad");
assert!(f("out_ch", 0) >= 1 && f("k", 0) >= 1 && f("stride", 0) >= 1, "{set_path} layer {i}: out_ch, k or stride of 0");
assert!(f("shift", 0) <= 63, "{set_path} layer {i}: a shift of 64 or more");
}
let lowered = wai_ops::lower::int_synth(&bytes).unwrap_or_else(|r| panic!("{set_path}: does not lower: {r:?}"));
let loaded = wai::int_ops::load(lowered.get("synth").unwrap());
if set_path.contains("/overflow_acc64/") {
assert!(loaded.is_err(), "{set_path}: the falsification set fails acc64");
} else {
assert!(loaded.is_ok(), "{set_path}: does not load under acc64: {loaded:?}");
}
sets += 1;
let Some(p) = payload_path else { continue };
let b = read(p);
let c = u16::from_le_bytes([b[8], b[9]]) as u64;
let n = c << (2 * b[10] as u64);
assert_eq!(b[11], 0, "{p}: the reserved byte");
assert!(c >= 1 && n <= 1 << 26 && b.len() as u64 == 12 + n, "{p}: C, the cap and the length");
assert!(wai::int_synth::decode_from_spec(&b, &bytes).is_ok(), "{p}: decodes through the lowering");
payloads += 1;
}
assert!(sets >= 11 && payloads >= 8, "the committed int_synth sets and payloads, at least those of this revision: {all:?}");
}