open-standards/wai/wai-rs/tests/int_kernels_geometry.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

320 lines
17 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
//! Geometry at the edges of what the fast integer kernels lay out, run through the
//! public API under the scalar reference kernels AND under each fast-kernel policy
//! (serial, explicit multi-threaded tilings, automatic):
//!
//! * a 1×1 input upsampled by strides from 1 up to `usize::MAX`, through the
//! transposed convolutions of int_hyper (a `WHM1` layer through its lowering), int_synth
//! (`decode_from_spec`) and the JPEG-AI synthesis (`conv_transpose`);
//! * a regular convolution whose stride reaches or exceeds its padded input
//! (a `WHM1` layer through its lowering, `conv_grouped`);
//! * padding far beyond the kernel, with output padding restoring a small output;
//! * an input of two or more rows and columns whose stride and padding make the
//! reference's own size arithmetic overflow (int_hyper, int_synth, JPEG-AI).
//!
//! The reference computes each of the first three in a handful of operations. Every
//! policy must return the same integers (checked against a hand computation), and
//! the fast path must not overflow, or allocate or loop in proportion to the stride
//! or the padding, where the reference does not. In the last, the reference panics,
//! and every policy must do the same. int_synth decodes through its lowering
//! (integer-operators §13.1), and the int_hyper networks through theirs (`whm1/1`,
//! §13.2): their fields are 32 bits and their run-time plan caps every size it forms at
//! 2^26, so where a case is past either, every policy refuses it, where the scalar kernel
//! the int_hyper decodes once ran panicked or computed. int_audio decodes through its
//! lowering `wam1/1` too; its 1-D transposed convolution's edge geometry is tested
//! through that graph under every policy (`int_audio` unit tests). CI runs this file
//! beside `int_kernels_vectors` on every `wai-conformance` leg, with overflow checks
//! on and off.
#![cfg(all(feature = "neural_int", feature = "legacy_int"))]
use serde_json::json;
use wai::int_kernels::{with_policy, Policy};
static POLICIES: std::sync::LazyLock<Vec<Policy>> = std::sync::LazyLock::new(wai::int_kernels::equivalence_policies);
/// A 3×3 kernel `[[1, 2, 3], [4, 5, 6], [7, 8, 9]]`.
const KERNEL: [i64; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
/// Run under every policy; all results must be identical. Returns the result.
fn same_under_every_policy<T: PartialEq + std::fmt::Debug>(what: &str, f: impl Fn() -> T) -> T {
let want = with_policy(POLICIES[0], &f);
for p in &POLICIES[1..] {
let got = with_policy(*p, &f);
assert!(got == want, "{what}: result under {p:?} differs from the reference kernels ({got:?} vs {want:?})");
}
want
}
/// Run under every policy, catching a panic. The outcome (the value, or that the
/// call panicked) must be the same under every policy. Returns the reference
/// kernels' outcome, `None` for a panic. The panic message is not compared: a fast
/// policy may fail at another element first, or on a worker thread.
fn same_outcome_under_every_policy<T: PartialEq + std::fmt::Debug>(what: &str, f: impl Fn() -> T) -> Option<T> {
let run = |p: Policy| std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| with_policy(p, &f))).ok();
let want = run(POLICIES[0]);
for p in &POLICIES[1..] {
let got = run(*p);
assert!(got == want, "{what}: outcome under {p:?} differs from the reference kernels' ({got:?} vs {want:?}; None = panicked)");
}
want
}
/// Strides from ordinary to the largest a `usize` holds. 2^29 fits the `u32` stride
/// field of a WHM1 model; from 2^32 (64-bit targets) the product of the two strides
/// no longer fits in 64 bits.
///
/// 2^29 comes first, deliberately. A kernel that allocates one tap table per
/// (row, column) phase — as the fast transposed kernel did before it built tables
/// only for phases holding output — fails there at once (the table count exceeds
/// what any allocation can hold: "capacity overflow"). Placed after 2^16 it would
/// first commit memory for 2^32 tables (hundreds of GiB) and drive the machine into
/// swap before failing.
fn strides() -> Vec<usize> {
#[allow(unused_mut)] // pushed to on 64-bit targets only
let mut s = vec![1 << 29, 1, 2, 3, 1 << 13, 1 << 16, usize::MAX];
#[cfg(target_pointer_width = "64")]
s.push(1 << 32);
s
}
/// A one-layer int_hyper model.
fn hyper_model(layer: serde_json::Value) -> wai::int_hyper_synth::IntModel {
wai::int_hyper_synth::IntModel::from_json(&json!({ "afrac": 12, "gf": 16, "shift": 0, "layers": [layer] }))
}
/// A one-layer int_hyper model run as the int_hyper decodes run their networks: lowered by
/// `whm1/1`, loaded under `acc64`, and run by the integer operator set with this crate's
/// fast kernels behind it (`int_ops`). The output and its shape `(c, h, w)`, or the
/// refusal's code: a field past 32 bits, or a size the run-time plan caps.
fn hyper_run(model: &wai::int_hyper_synth::IntModel, x: &[i64], c: usize, h: usize, w: usize) -> Result<(Vec<i64>, usize, usize, usize), wai_ops::Code> {
let net = wai::int_ops::whm1_net(model).map_err(|r| r.code)?;
let l = wai_ops::lower::whm1_net_loaded(net, false, wai_ops::Profile::Acc64).map_err(|r| r.code)?;
let t = wai::int_ops::run1(&l, wai_ops::Tensor::new(c as u32, h as u32, w as u32, x.to_vec()), &[]).map_err(|r| r.code)?;
Ok((t.data, t.c as usize, t.h as usize, t.w as usize))
}
/// An int_synth payload: a 1×1 output image from one int8 latent (C = 1, S = 1).
fn synth_payload(latent: i8) -> Vec<u8> {
let mut p = Vec::new();
p.extend(1u32.to_le_bytes()); // H
p.extend(1u32.to_le_bytes()); // W
p.extend(1u16.to_le_bytes()); // C
p.extend([0u8, 0]); // S_log2, reserved
p.push(latent as u8);
p
}
/// A one-layer int_synth spec: 1 → 3 channels, 3×3 kernel `1..=27` (channel `oc`
/// holds `9·oc + KERNEL`), shift 0, no ReLU.
fn synth_spec(stride: usize, pad: usize, out_pad: usize, bias: [i32; 3]) -> Vec<u8> {
let weight: Vec<i32> = (1..=27).collect();
json!({ "layers": [{
"in_ch": 1, "out_ch": 3, "k": 3, "stride": stride, "pad": pad, "out_pad": out_pad,
"weight": weight, "bias": bias, "shift": 0, "relu": false
}]})
.to_string()
.into_bytes()
}
#[test]
fn a_1x1_input_upsampled_by_any_stride() {
// k = 3, pad 1: the output is 1×1 and receives only the centre tap, 5·x.
for s in strides() {
let got = same_under_every_policy(&format!("JPEG-AI conv_transpose, stride {s}"), || {
wai::int_transformer::conv_transpose(&[5], 1, 1, 1, &KERNEL, 1, 3, s, 1, 0, &[1], &[0], 0)
});
assert_eq!(got, (vec![25], 1, 1), "JPEG-AI conv_transpose, stride {s}");
let model = hyper_model(json!({
"type": "convT", "out_ch": 1, "k": 3, "stride": s, "pad": 1, "opad": 0,
"weight": KERNEL, "mult": [1], "bias": [0]
}));
let got = same_under_every_policy(&format!("int_hyper convT, stride {s}"), || hyper_run(&model, &[5], 1, 1, 1));
if s <= u32::MAX as usize {
assert_eq!(got, Ok((vec![25], 1, 1, 1)), "int_hyper convT, stride {s}");
} else {
// A WHM1 field is 32 bits: a model past it is not a WHM1 network.
assert_eq!(got, Err(wai_ops::Code::ModelMismatch), "int_hyper convT, stride {s}");
}
let (payload, spec) = (synth_payload(5), synth_spec(s, 1, 0, [0, 0, 0]));
let got = same_under_every_policy(&format!("int_synth, stride {s}"), || {
wai::int_synth::decode_from_spec(&payload, &spec).map(|img| img.rgb)
});
if s <= u32::MAX as usize {
// centre taps 5, 14, 23 of the three output channels, times 5
assert_eq!(got, Ok(vec![25, 70, 115]), "int_synth, stride {s}");
} else {
// int_synth decodes through its lowering, whose `WHG1` fields are 32 bits: a
// stride past them is refused, a planned break.
assert!(got.is_err(), "int_synth, stride {s}: a stride past 32 bits is refused");
}
}
}
#[test]
fn a_regular_conv_whose_stride_exceeds_its_padded_input() {
// 5×5 input 1..=25, k = 3. Pad 1 (7×7 padded): from stride 5 on, one output,
// the taps that meet x[0], x[1], x[5], x[6]: 5·1 + 6·2 + 8·6 + 9·7. Pad 2 (9×9
// padded): from stride 7 on, one output, only tap (2, 2) meets x[0]: 9·1. At
// stride usize::MAX, pad 2 puts two leading column phases before the first input
// column, whose position must be found without overflowing.
let x: Vec<i64> = (1..=25).collect();
for (pad, want, first) in [(1usize, 5 + 6 * 2 + 8 * 6 + 9 * 7, 5usize), (2, 9, 7)] {
#[allow(unused_mut)] // pushed to on 64-bit targets only
let mut ss = vec![first, 7, 8, 9, 1 << 13, 1 << 16, 1 << 29, usize::MAX];
#[cfg(target_pointer_width = "64")]
ss.push(1 << 32);
for s in ss {
let got = same_under_every_policy(&format!("JPEG-AI conv_grouped, pad {pad}, stride {s}"), || {
wai::int_transformer::conv_grouped(&x, 1, 5, 5, &KERNEL, 1, 3, s, pad, 1, &[1], &[0], 0)
});
assert_eq!(got, (vec![want], 1, 1), "JPEG-AI conv_grouped, pad {pad}, stride {s}");
let model = hyper_model(json!({
"type": "conv", "out_ch": 1, "k": 3, "stride": s, "pad": pad,
"weight": KERNEL, "mult": [1], "bias": [0]
}));
let got = same_under_every_policy(&format!("int_hyper conv, pad {pad}, stride {s}"), || hyper_run(&model, &x, 1, 5, 5));
if s <= u32::MAX as usize {
assert_eq!(got, Ok((vec![want], 1, 1, 1)), "int_hyper conv, pad {pad}, stride {s}");
} else {
assert_eq!(got, Err(wai_ops::Code::ModelMismatch), "int_hyper conv, pad {pad}, stride {s}");
}
}
}
}
#[test]
fn padding_far_beyond_the_kernel() {
let p = 1usize << 12;
// Transposed: pad 2^12 with output padding 2^13 − 2 restores a 1×1 output that
// no tap reaches (it sits 2^12 rows into the uncropped plane). int_synth decodes
// through its lowering, whose run-time plan caps the uncropped output a transposed
// convolution forms at 2^26 elements (integer-operators §5.3); this one is
// 3·8193·8193, so every policy refuses it before anything is allocated, a planned
// break. With pad 2^9 and output padding 2^10 − 2 the uncropped output, 3·1025·1025,
// is within the cap, and the decode gives the bias.
let (payload, spec) = (synth_payload(5), synth_spec(1, p, 2 * p - 2, [10, 20, 30]));
let got = same_under_every_policy("int_synth, pad 2^12", || {
wai::int_synth::decode_from_spec(&payload, &spec).map(|img| img.rgb)
});
assert!(got.is_err(), "int_synth, pad 2^12: the uncropped output is past the cap");
let q = 1usize << 9;
let (payload, spec) = (synth_payload(5), synth_spec(1, q, 2 * q - 2, [10, 20, 30]));
let got = same_under_every_policy("int_synth, pad 2^9", || {
wai::int_synth::decode_from_spec(&payload, &spec).map(|img| img.rgb)
});
assert_eq!(got, Ok(vec![10, 20, 30]));
// Regular: a 1×1 input, 1×1 kernel, pad 2^12, stride 2^13 — a 2×2 output whose
// samples all fall in the padding, so each is the bias.
let got = same_under_every_policy("JPEG-AI conv_grouped, pad 2^12", || {
wai::int_transformer::conv_grouped(&[5], 1, 1, 1, &[3], 1, 1, 2 * p, p, 1, &[1], &[7], 0)
});
assert_eq!(got, (vec![7; 4], 2, 2));
let model = hyper_model(json!({
"type": "conv", "out_ch": 1, "k": 1, "stride": 2 * p, "pad": p, "weight": [3], "mult": [1], "bias": [7]
}));
let got = same_under_every_policy("int_hyper conv, pad 2^12", || hyper_run(&model, &[5], 1, 1, 1));
assert_eq!(got, Ok((vec![7; 4], 1, 2, 2)));
}
/// An int_synth payload: an `h`×`w` output image from a 2×2 int8 latent (C = 1,
/// S = 2), latents in row-major order.
#[cfg(target_pointer_width = "64")]
fn synth_payload_2x2(h: u32, w: u32, latents: [i8; 4]) -> Vec<u8> {
let mut p = Vec::new();
p.extend(h.to_le_bytes());
p.extend(w.to_le_bytes());
p.extend(1u16.to_le_bytes()); // C
p.extend([1u8, 0]); // S_log2 = 1, reserved
p.extend(latents.map(|v| v as u8));
p
}
// Transposed convolutions of an input with two or more rows and columns, whose
// stride and padding make the reference's own size arithmetic overflow. The
// int_hyper, int_audio and JPEG-AI references size an uncropped plane
// `cout·(ho + 2·pad)·(wo + 2·pad)` in unchecked `usize` arithmetic. With overflow
// checks on, the reference panics on that arithmetic. With them off it wraps and then
// indexes out of the wrapped buffer. (int_synth decodes through its lowering, whose
// plan forms every size in checked 64-bit arithmetic and caps it: it refuses these,
// below.) A fast policy must have
// the same outcome: it may not return a value where the reference panics. A 1×1
// input cannot show this, since `(h − 1)·stride` is 0. The int_audio 1-D form is
// tested through its lowering's graph (`int_audio` unit tests).
/// A 3×3 input, and a 2×2 one with fields that fit a WHM1 model's `u32`.
const X: [i64; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
/// Stride 2^32 − 1 and pad 2^31 − 1: a 2×2 output from a 2×2 input, k = 1 (or a
/// 4×4 one, k = 3), under an uncropped plane of 2^32 × 2^32. On a 64-bit target
/// the plane size overflows; on a 32-bit one the stride arithmetic already does.
const S32: usize = u32::MAX as usize;
const P32: usize = (u32::MAX >> 1) as usize;
#[test]
fn jpegai_conv_transpose_where_the_reference_plane_size_overflows() {
// 3×3 input, k = 1, stride = pad = 2^62: a 1×1 output, but an uncropped plane
// of (2^63 + 1)² elements. The reference panics in both profiles.
#[cfg(target_pointer_width = "64")]
{
let s = 1usize << 62;
let got = same_outcome_under_every_policy("JPEG-AI conv_transpose, 3×3, stride = pad = 2^62", || {
wai::int_transformer::conv_transpose(&X, 1, 3, 3, &[7], 1, 1, s, s, 0, &[1], &[0], 0)
});
assert_eq!(got, None, "the reference panics");
}
let got = same_outcome_under_every_policy("JPEG-AI conv_transpose, 2×2, u32 stride and pad", || {
wai::int_transformer::conv_transpose(&X[..4], 1, 2, 2, &[7], 1, 1, S32, P32, 0, &[1], &[0], 0)
});
assert_eq!(got, None, "u32 fields: the reference panics");
}
#[test]
fn int_hyper_convt_where_the_reference_plane_size_overflows() {
// As above, through a WHM1 layer's lowering, as the int_hyper decodes run it. The
// scalar kernel they once ran panicked on both; through the operator set, every policy
// refuses both before anything is allocated: 2^62 is past a WHM1 field's 32 bits, and
// the u32 stride and pad give an uncropped side, (2 − 1)·(2^32 − 1) + 1, past the cap.
let convt = |s: usize, p: usize| {
hyper_model(json!({
"type": "convT", "out_ch": 1, "k": 1, "stride": s, "pad": p, "opad": 0,
"weight": [7], "mult": [1], "bias": [0]
}))
};
#[cfg(target_pointer_width = "64")]
{
let model = convt(1 << 62, 1 << 62);
let got = same_outcome_under_every_policy("int_hyper convT, 3×3, stride = pad = 2^62", || hyper_run(&model, &X, 1, 3, 3));
assert_eq!(got, Some(Err(wai_ops::Code::ModelMismatch)), "refused, not panicked");
}
let model = convt(S32, P32);
let got = same_outcome_under_every_policy("int_hyper convT, 2×2, u32 stride and pad", || hyper_run(&model, &X[..4], 1, 2, 2));
assert!(matches!(got, Some(Err(_))), "u32 fields: refused by the cap, not panicked: {got:?}");
}
#[cfg(target_pointer_width = "64")]
#[test]
fn int_synth_where_the_reference_output_size_overflows_i64() {
// 2×2 latent, k = 3, stride = pad = out_pad = 2^62: a 3×3 output. The direct
// decode computed (2 − 1)·2^62 + 3 + 2^62 − 2·2^62 in `i64`, which overflows
// (2^63 + 3) although every `usize` step fits: it panicked with overflow checks on
// and, with them off, returned the wrapped size's output. int_synth decodes through
// its lowering (integer-operators §13.1), whose `WHG1` fields are 32 bits: in every
// build and under every policy the decode is refused, not panicked. A planned break.
let s = 1usize << 62;
let (payload, spec) = (synth_payload_2x2(3, 3, [1, 2, 3, 4]), synth_spec(s, s, s, [0, 0, 0]));
let got = same_outcome_under_every_policy("int_synth, 2×2 latent, stride = pad = out_pad = 2^62", || {
wai::int_synth::decode_from_spec(&payload, &spec).map(|img| img.rgb)
});
assert!(matches!(got, Some(Err(_))), "refused, not panicked: {got:?}");
// The u32 fields fit `WHG1`, but the uncropped side (2 − 1)·(2^32 − 1) + 3 is past
// the run-time plan's cap of 2^26: refused before anything is allocated (the direct
// decode returned the bias), a planned break.
let (payload, spec) = (synth_payload_2x2(4, 4, [1, 2, 3, 4]), synth_spec(S32, P32, 0, [10, 20, 30]));
let got = same_outcome_under_every_policy("int_synth, 2×2 latent, u32 stride and pad", || {
wai::int_synth::decode_from_spec(&payload, &spec).map(|img| img.rgb)
});
assert!(matches!(got, Some(Err(_))), "u32 fields: refused by the cap: {got:?}");
}