wai-ops/1-i32 on GPUs: WGSL kernels exact at acc32 and acc64, and the tier on the adapters a run showed equal (G1) #380

Merged
dcharlot merged 12 commits from wai/ops-gpu into main 2026-10-21 07:12:52 -04:00
Owner

G1: WAI's integer operators (wai-ops/1-i32) run on GPUs, and determinism-tiers claims the tier exactly as far as runs showed it. This closes the line "No decoder of these capabilities runs on either [GPU or NPU] in this revision, and the tier is not claimed there".

What is in it

  • wai/wai-ops-gpu: wai-ops/1-i32 (integer-operators §9.1) as WGSL compute kernels through wgpu, so Metal, Vulkan and DX12 natively, and a browser's WebGPU.

    • WGSL has no 64-bit integer, so the kernels use 32-bit words. A graph whose certificate meets acc32 runs one word per value (its product intermediates formed from 16-bit halves).
    • Every other graph runs exact 64-bit and 128-bit arithmetic built from 32-bit words, an acc64 implementation (integer-operators §7.3).
    • Graphs with layernorm_c, l2norm, softmax, sigmoid or elu run on the CPU, with the reason named.
    • The host makes the interpreter's run-time checks before the device runs anything. A check tensor that leaves its range refuses the decode at its operator (field_range).
  • wai-rs ops_gpu (off by default): int_ops::run_graph is where every integer decoder runs a graph. Inside int_ops::gpu::with_gpu it runs each graph on the device. with_gpu_compared also runs the interpreter and compares every graph.

  • Tests that do not pass on a skip:

    • the arithmetic probe: 1,051,757 cases against 128-bit host arithmetic;
    • every ops-conformance/ graph vector the subset covers, at both widths;
    • 18 shader mutants (a rounding shift or its addend off by one, a tie rule, a clamp bound, a dropped carry or cross term, a sign rule, a post bias, an index rule), each of which the corpus must catch;
    • wai-rs/tests/ops_gpu.rs decoding the six integer capabilities' committed vectors and refusal fixtures with their graphs on the device.

    With WAI_OPS_GPU_REQUIRE=1 an absent adapter fails.

  • determinism-tiers §5 and §6: the tier is claimed for wai-ops/1-i32 graphs on the adapters a run showed equal. Nothing is claimed for another adapter, backend or driver version, nothing for DX12, and nothing for NPUs. The adapters:

    • Metal, Apple arm64, macOS 27.0.1;
    • headless Chromium 148, and a Chromium-based browser at 155, on that GPU (the corpus, not the decoders);
    • Vulkan on a 0x10de discrete GPU (driver 595.84) and a 0x8086 integrated GPU (Mesa 25.2.8);
    • Vulkan on a 0x1002 integrated GPU (Mesa 26.0.8).
  • tools/ops-gpu-bench: time, and energy where the host has a counter, per capability, on the CPU and on each adapter, every decode compared byte for byte with the CPU's.

  • CI: wai-ops-gpu.yml runs the Metal and browser legs on the macos runner, gated on this PR's own diff.

Checks

run result
wai-ops-gpu: cargo test --locked --release with WAI_OPS_GPU_REQUIRE=1 on the build host's Metal GPU (re-run by the lead 2026-10-21) arithmetic probe ok; every subset vector equal to the interpreter's; every mutant caught
wai-rs --features ops_gpu,video_int,audio_int,splat_int,world_int --test ops_gpu (same host) ok
clippy, wai-ops-gpu --all-targets no notes
scripts/check-lockfiles.sh 47 lockfiles, all current
repo scripts (tests in CI, workflow concurrency, spec refs, section refs, vanity, comparative prose, orphan sources) all 0
Vulkan legs (NVIDIA, Intel, AMD) and the browser leg run by the slice's builder on 2026-10-20 and 21, recorded in determinism-tiers §6 with adapter IDs and driver versions; not re-run for this PR

Speed and energy (ops-gpu-bench --secs 3 --min-runs 5, build host, Metal, 2026-10-21)

These are the committed test vectors, which are tiny. They measure dispatch overhead more than throughput, and no speed claim is made.

capability · vector CPU median GPU median graphs on GPU
int_hyper · tiny 0.037 ms 8.27 ms 2
video int_hyper · keyframe 0.122 ms 8.06 ms 2
audio int_codec 4.49 ms 1.04 ms 1
splat int_codec 1.69 ms 8.86 ms 1
int_synth · photo 91.9 ms 39.0 ms 1
int_mlicpp 1.13 ms 57.0 ms 10
world model 53.5 ms 161 ms 17
  • The GPU path wins where a graph is large (audio, the photo-sized synthesis) and loses where per-graph dispatch dominates.
  • Energy: the CPU legs read 1–5.5 J per 3 s window (this process's CPU energy, IOReport). The GPU legs read the GPU rail as none, because another process used the GPU during each window: the host runs CI.
  • Throughput at real resolution, and joules per decode on an otherwise idle GPU, are the next measurements. Batching graphs per dispatch is the obvious engineering step.

Not done

  • NPUs (wai-ops/1-npu on XDNA and others): a follow-up slice.
  • DX12.
  • The browser leg runs the corpus, not the decoders.
  • No forge run of wai-ops-gpu.yml yet: this PR is its first.

🤖 Generated with Claude Code

G1: WAI's integer operators (`wai-ops/1-i32`) run on GPUs, and determinism-tiers claims the tier exactly as far as runs showed it. This closes the line "No decoder of these capabilities runs on either [GPU or NPU] in this revision, and the tier is not claimed there". ## What is in it - **`wai/wai-ops-gpu`:** `wai-ops/1-i32` (integer-operators §9.1) as WGSL compute kernels through wgpu, so Metal, Vulkan and DX12 natively, and a browser's WebGPU. - WGSL has no 64-bit integer, so the kernels use 32-bit words. A graph whose certificate meets `acc32` runs one word per value (its `product` intermediates formed from 16-bit halves). - Every other graph runs exact 64-bit and 128-bit arithmetic built from 32-bit words, an `acc64` implementation (integer-operators §7.3). - Graphs with `layernorm_c`, `l2norm`, `softmax`, `sigmoid` or `elu` run on the CPU, with the reason named. - The host makes the interpreter's run-time checks before the device runs anything. A `check` tensor that leaves its range refuses the decode at its operator (`field_range`). - **wai-rs `ops_gpu`** (off by default): `int_ops::run_graph` is where every integer decoder runs a graph. Inside `int_ops::gpu::with_gpu` it runs each graph on the device. `with_gpu_compared` also runs the interpreter and compares every graph. - **Tests that do not pass on a skip:** - the arithmetic probe: 1,051,757 cases against 128-bit host arithmetic; - every `ops-conformance/` graph vector the subset covers, at both widths; - 18 shader mutants (a rounding shift or its addend off by one, a tie rule, a clamp bound, a dropped carry or cross term, a sign rule, a post bias, an index rule), each of which the corpus must catch; - `wai-rs/tests/ops_gpu.rs` decoding the six integer capabilities' committed vectors and refusal fixtures with their graphs on the device. With `WAI_OPS_GPU_REQUIRE=1` an absent adapter fails. - **determinism-tiers §5 and §6:** the tier is claimed for `wai-ops/1-i32` graphs on the adapters a run showed equal. Nothing is claimed for another adapter, backend or driver version, nothing for DX12, and nothing for NPUs. The adapters: - Metal, Apple arm64, macOS 27.0.1; - headless Chromium 148, and a Chromium-based browser at 155, on that GPU (the corpus, not the decoders); - Vulkan on a 0x10de discrete GPU (driver 595.84) and a 0x8086 integrated GPU (Mesa 25.2.8); - Vulkan on a 0x1002 integrated GPU (Mesa 26.0.8). - **`tools/ops-gpu-bench`:** time, and energy where the host has a counter, per capability, on the CPU and on each adapter, every decode compared byte for byte with the CPU's. - **CI:** `wai-ops-gpu.yml` runs the Metal and browser legs on the `macos` runner, gated on this PR's own diff. ## Checks | run | result | |---|---| | `wai-ops-gpu`: `cargo test --locked --release` with `WAI_OPS_GPU_REQUIRE=1` on the build host's Metal GPU (re-run by the lead 2026-10-21) | arithmetic probe ok; every subset vector equal to the interpreter's; every mutant caught | | `wai-rs --features ops_gpu,video_int,audio_int,splat_int,world_int --test ops_gpu` (same host) | ok | | clippy, `wai-ops-gpu --all-targets` | no notes | | `scripts/check-lockfiles.sh` | 47 lockfiles, all current | | repo scripts (tests in CI, workflow concurrency, spec refs, section refs, vanity, comparative prose, orphan sources) | all 0 | | Vulkan legs (NVIDIA, Intel, AMD) and the browser leg | run by the slice's builder on 2026-10-20 and 21, recorded in determinism-tiers §6 with adapter IDs and driver versions; not re-run for this PR | ## Speed and energy (`ops-gpu-bench --secs 3 --min-runs 5`, build host, Metal, 2026-10-21) These are the committed **test vectors**, which are tiny. They measure dispatch overhead more than throughput, and **no speed claim is made**. | capability · vector | CPU median | GPU median | graphs on GPU | |---|---|---|---| | int_hyper · tiny | 0.037 ms | 8.27 ms | 2 | | video int_hyper · keyframe | 0.122 ms | 8.06 ms | 2 | | audio int_codec | 4.49 ms | **1.04 ms** | 1 | | splat int_codec | 1.69 ms | 8.86 ms | 1 | | int_synth · photo | 91.9 ms | **39.0 ms** | 1 | | int_mlicpp | 1.13 ms | 57.0 ms | 10 | | world model | 53.5 ms | 161 ms | 17 | - The GPU path wins where a graph is large (audio, the photo-sized synthesis) and loses where per-graph dispatch dominates. - Energy: the CPU legs read 1–5.5 J per 3 s window (this process's CPU energy, IOReport). The GPU legs read the GPU rail as `none`, because another process used the GPU during each window: the host runs CI. - Throughput at real resolution, and joules per decode on an otherwise idle GPU, are the next measurements. Batching graphs per dispatch is the obvious engineering step. ## Not done - NPUs (`wai-ops/1-npu` on XDNA and others): a follow-up slice. - DX12. - The browser leg runs the corpus, not the decoders. - No forge run of `wai-ops-gpu.yml` yet: this PR is its first. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The integer operator set's graph operators on GPUs. WGSL has no 64-bit
integer, so the kernels use 32-bit words only: a graph whose certificate
meets acc32 runs on one word per value, with its two product intermediates
formed from 16-bit halves; every other graph of wai-ops/1-i32 meets acc64 and
runs on exact 64- and 128-bit arithmetic built from 32-bit words
(integer-operators §7.3). A graph with layernorm_c, l2norm, softmax, sigmoid
or elu runs on the CPU, and support() says which operator sent it there.

The host makes the interpreter's run-time checks in its order before the
device runs anything; a check tensor that leaves its range sets its
operator's flag, and the first flagged operator refuses the decode. Every
device passes an arithmetic probe against the host before it runs a graph.

Tests: the probe on a million cases, an edited product caught by it, and
every graph vector of ops-conformance that the subset covers, at both
widths, held to the committed expectations and the CPU interpreter.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
tests/mutants.rs runs the corpus on a device compiled from an edited kernel:
a rounding shift or its addend off by one, a tie rule flipped, a clamp bound
moved by one, a check that misses values above its range, a dropped carry or
cross term of a product, a sign rule, a post bias dropped, an index rule of
the transposed convolution, the convolution's padding edge, igdn's term
floor, the leaky shift of 32 or more, and two data-movement orders. Each must
give a result that differs from the committed one.

tests/corpus.rs also runs the 32-bit kernels on the graphs whose
certificate does not meet acc32, through a hidden entry point (run_at
refuses them): some differ from the interpreter, so the width a graph runs
at is load-bearing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
int_ops::run_graph is now the one place the integer decoders run a graph
(run1 and jpegai_ops's networks go through it). With the ops_gpu feature,
inside int_ops::gpu::with_gpu, it runs each graph on a wai_ops_gpu device at
the width its certificate admits, and on the interpreter where the device
does not run it (an operator outside wai-ops/1-i32), counting both by width
and by reason. Off by default; a default build has no GPU interface.

tests/ops_gpu.rs decodes every committed vector and refusal fixture of every
capability that runs a wai-ops/1 graph with its graphs on each adapter found,
and requires the CPU decode's bytes or refusal code: int_hyper and its video
keyframe, the audio and splat codecs, int_synth, int_mlicpp, the world
renderer, and wai.image.jpegai's exact decoder on the jpegai/v1 inputs when
they are given.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A separate crate with its own lockfile, as quantum-meter is. Per committed
vector and per path (the CPU's default kernels on every core, then each
adapter found): two warm-up decodes, then decodes back to back for at least
the given seconds and count, each compared byte for byte with the CPU's
output; the line gives the count, median and 10th/90th percentiles. Energy is
read at the two ends of the timed loop only: on macOS this process's CPU
energy and GPU share (IOReport, which gives no GPU figure where the GPU was
shared) with the GPU rail over the window as an upper bound; on Linux RAPL
package-0 and, with the NVIDIA driver, its cumulative board energy counter,
after an idle window that gives each counter's idle power.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
browser/ is a wasm32 module that runs every graph vector of ops-conformance
on the WebGPU adapter a page is offered, the kernels compiled by the
browser's own WGSL compiler, and holds each decode to the committed result
and to the interpreter running in the same page. run.mjs serves it with the
corpus and drives a headless browser (on macOS through Metal), and fails on a
mismatch, a device error or a page error, or, under WAI_OPS_GPU_REQUIRE=1,
when no adapter ran the corpus.

tests/support/corpus.rs now parses a file apart from reading it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The metal job runs the arithmetic probe, the corpus at both widths and the
mutants on the host's GPU, every capability that runs a wai-ops/1 graph with
its graphs there (the jpegai/v1 inputs fetched and required), and the corpus
in a headless browser's WebGPU. WAI_OPS_GPU_EXPECT declares the adapter and
WAI_OPS_GPU_REQUIRE makes its absence a failure; each step then requires its
report lines with their counts. The checks job, on a runner with no GPU,
requires that the device tests skip without WAI_OPS_GPU_REQUIRE and fail with
it, and builds the browser module, wai-rs's GPU target and the bench tool.

The arith test now appends its result line to the report as the others do.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The capability test now holds the device to the interpreter graph by graph,
as well as through each decode's bytes: every graph the device ran was
compared, and none differed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
§5's GPU bullet now meets the tier for a decoder's wai-ops/1-i32 graphs on
the adapters and backends a run showed equal, lists them by backend, PCI
vendor and device and driver, and claims nothing for any other adapter or for
an NPU; jpegai's scale path keeps its entropy-consistency claim there, and
its synthesis graphs run on the CPU. §6 records the runs: the probe, the
corpus at both widths, the width test, the mutants, and the capability
decodes compared graph by graph. SPEC.md's fabric sentence and the
integer-operators header point to it; CHANGELOG entry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge origin/main into wai/ops-gpu
All checks were successful
wai-ops integer operators / wasm32 under Node (pull_request) Successful in 25s
wai-ops integer operators / independent checker (Python) (pull_request) Successful in 14s
wai-pack / wai-pack · linux (pull_request) Successful in 1m42s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m57s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m52s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 24s
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Successful in 26m11s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 4m15s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Successful in 3m14s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 2m14s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 5m32s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 12m32s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 28m27s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 18m7s
wai-ops integer operators / linux-aarch64 (pull_request) Successful in 1m20s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m15s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 21s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m9s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 4m1s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 33m44s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 15m15s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 18s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 42s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 6m6s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 2m37s
wai-ops integer operators / macos-arm64 (pull_request) Successful in 57s
wai-pack / wai-pack · macos (pull_request) Successful in 1m8s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 2m21s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 5m17s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 2m4s
03cbb8ff77
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!380
No description provided.