wai-quantum 0.4.2: tile runs certified on their own, and the WebGPU member in a browser #225

Merged
dcharlot merged 5 commits from wai-quantum-webgpu-tile into main 2026-10-12 15:27:57 -04:00
Owner

Why

0.4.1 put one WGSL gate kernel on Metal, Vulkan and DX12, certified bit-exact against the CPU reference on each adapter. This adds the two pieces that follow from it:

  • The same member in a browser. The browser compiles the kernel with its own WGSL compiler, so the browser leg certifies the bit-exactness argument on a translator independent of the native one.
  • A faster implementation with the same bits. Runs of gates that stay inside a tile are fused into one dispatch. It is certified on its own against the same pinned digests, and the per-gate implementation stays as the reference and the fallback.

What

The member in a browser (wai-web feature quantum_webgpu, a bundle of its own, --out-name wai_quantum_webgpu)

  • quantum_webgpu_discover and QuantumWebGpu (certify, statevector_hash_hex) for JS. The default wai-web build and its bundle are unchanged.
  • demo/quantum_webgpu.html runs the member in a worker.
  • quantum-conformance/browser_webgpu.mjs drives the page headless and requires:
    • four certificates bit-exact with the pinned digests;
    • every vector's statevector hash;
    • every reject refused with its class;
    • a too-wide circuit refused. The adapter is asked for the WebGPU default limits, so the member holds 25 qubits and refuses 26 through the real code path.
  • quantum-conformance/fabric/expected.json lists the four certificates and both implementation ids. A crate test (tests/fabric_expected.rs, not in the package) holds the file to the crate exactly, so the page's pins cannot be weakened by editing it alone.
  • A browser CI job on macOS.

Tile runs: wgsl-lanes32-tile/54e443520c020013

  • plan::Config (PER_GATE, TILED), Step::Tile, compile_with, schedule, execute_from; PLAN_REVISION is 2.

  • A tiled plan applies each maximal run of consecutive gates that act inside a tile to tiles of up to 1024 amplitudes, in op-log order. Those are diagonal gates on any target, and others with their target inside the tile.

  • Only exact identities are left out. circuit/1's P(19..=32) is 2^30 − 1, not the identity, and stays.

  • The CPU executor mirrors the kernel's index arithmetic and equals the per-gate program on any words.

  • lanes32_tile.wgsl:

    • one workgroup per tile, the tile updated in place in the state planes;
    • a storage barrier between gates, in uniform control flow;
    • u32 only, literal shifts only.

    The plan's tile kernel used workgroup memory. The SPIR-V validator rejects the explicit layouts that the shader translator puts on workgroup arrays, so the kernel was restructured rather than the defect waived.

  • A run record whose tiles or grid do not cover the state exactly once is refused. So is a gate whose diag disagrees with its words, or an op index of u32::MAX.

  • Tile steps are split into dispatches of at most SUBMIT_BUDGET pair-updates.

  • The checker holds the tile shader to its own rules, including a uniformity taint check on the barrier, and its SPIR-V to the validator. Three tile mutants each have a named detector, including a race witness for a missing barrier.

  • WebGpuMember::tiled() runs the tile implementation on the same device; it is probed and certified on its own.

  • Placement: given both implementations' entries for one member and no energy figures, place picks the tile implementation, because its id sorts first. Discovery opens per-gate members, which remain the fallback.

Review

An independent review of both stages found no input on which either implementation returns bits other than the reference's. It ran:

  • an independent model of the in-place tiling, checking ownership, disjointness, bounds and equality with the per-gate program on random cases including ragged grids;
  • the WGSL barrier semantics, read from the spec text;
  • live browser runs of both shaders.

Its findings are fixed:

  • the page's pins are now authoritative;
  • the checker gains the uniformity check;
  • the op index u32::MAX is refused;
  • diag is validated;
  • tile dispatches respect the submission budget;
  • the device tests gain tile coverage (nonzero starts, a flag in a later submission, a split run, and the wide sizes in the WIDE block);
  • malformed run records are refused;
  • the placement tie is documented;
  • a path filter is added.

Checked locally (Metal)

  • The member script passes: both implementations reproduce the four pinned digests, the conformance vectors match, every mutant is caught (7 of 7, tile 3 of 3), and repeated runs agree.
  • The browser leg passes, headless: 4 of 4 per-gate and 4 of 4 tile certificates, every vector under both, and every reject refused.
  • The checker passes, and spirv-val accepts both shaders. Planted non-uniform barriers fail the checker, and the browser rejects them too.
  • wai-quantum release and wasm32-wasip2 gates pass, and the package carries both shaders.

Timing is printed and never asserted, from a loaded debug build. The tiled kernel is much faster on long circuits over small registers and faster at 16 and 22 qubits on random circuits. It is slower on some 16- and 20-qubit circuits, so placement should rest on measured energy, which a later stage attaches.

wai-quantum 0.4.2 is additive: default features, full, the per-gate implementation and every existing pin are unchanged.

🤖 Generated with Claude Code

## Why 0.4.1 put one WGSL gate kernel on Metal, Vulkan and DX12, certified bit-exact against the CPU reference on each adapter. This adds the two pieces that follow from it: - **The same member in a browser.** The browser compiles the kernel with its own WGSL compiler, so the browser leg certifies the bit-exactness argument on a translator independent of the native one. - **A faster implementation with the same bits.** Runs of gates that stay inside a tile are fused into one dispatch. It is certified on its own against the same pinned digests, and the per-gate implementation stays as the reference and the fallback. ## What **The member in a browser** (wai-web feature `quantum_webgpu`, a bundle of its own, `--out-name wai_quantum_webgpu`) - `quantum_webgpu_discover` and `QuantumWebGpu` (`certify`, `statevector_hash_hex`) for JS. The default wai-web build and its bundle are unchanged. - `demo/quantum_webgpu.html` runs the member in a worker. - `quantum-conformance/browser_webgpu.mjs` drives the page headless and requires: - four certificates bit-exact with the pinned digests; - every vector's statevector hash; - every reject refused with its class; - a too-wide circuit refused. The adapter is asked for the WebGPU default limits, so the member holds 25 qubits and refuses 26 through the real code path. - `quantum-conformance/fabric/expected.json` lists the four certificates and both implementation ids. A crate test (`tests/fabric_expected.rs`, not in the package) holds the file to the crate exactly, so the page's pins cannot be weakened by editing it alone. - A `browser` CI job on macOS. **Tile runs: `wgsl-lanes32-tile/54e443520c020013`** - `plan::Config` (`PER_GATE`, `TILED`), `Step::Tile`, `compile_with`, `schedule`, `execute_from`; `PLAN_REVISION` is 2. - A tiled plan applies each maximal run of consecutive gates that act inside a tile to tiles of up to 1024 amplitudes, in op-log order. Those are diagonal gates on any target, and others with their target inside the tile. - Only exact identities are left out. circuit/1's `P(19..=32)` is `2^30 − 1`, not the identity, and stays. - The CPU executor mirrors the kernel's index arithmetic and equals the per-gate program on any words. - `lanes32_tile.wgsl`: - one workgroup per tile, the tile updated in place in the state planes; - a storage barrier between gates, in uniform control flow; - u32 only, literal shifts only. The plan's tile kernel used workgroup memory. The SPIR-V validator rejects the explicit layouts that the shader translator puts on workgroup arrays, so the kernel was restructured rather than the defect waived. - A run record whose tiles or grid do not cover the state exactly once is refused. So is a gate whose `diag` disagrees with its words, or an op index of `u32::MAX`. - Tile steps are split into dispatches of at most `SUBMIT_BUDGET` pair-updates. - The checker holds the tile shader to its own rules, including a uniformity taint check on the barrier, and its SPIR-V to the validator. Three tile mutants each have a named detector, including a race witness for a missing barrier. - `WebGpuMember::tiled()` runs the tile implementation on the same device; it is probed and certified on its own. - Placement: given both implementations' entries for one member and no energy figures, `place` picks the tile implementation, because its id sorts first. Discovery opens per-gate members, which remain the fallback. ## Review An independent review of both stages found no input on which either implementation returns bits other than the reference's. It ran: - an independent model of the in-place tiling, checking ownership, disjointness, bounds and equality with the per-gate program on random cases including ragged grids; - the WGSL barrier semantics, read from the spec text; - live browser runs of both shaders. Its findings are fixed: - the page's pins are now authoritative; - the checker gains the uniformity check; - the op index `u32::MAX` is refused; - `diag` is validated; - tile dispatches respect the submission budget; - the device tests gain tile coverage (nonzero starts, a flag in a later submission, a split run, and the wide sizes in the WIDE block); - malformed run records are refused; - the placement tie is documented; - a path filter is added. ## Checked locally (Metal) - The member script passes: both implementations reproduce the four pinned digests, the conformance vectors match, every mutant is caught (7 of 7, tile 3 of 3), and repeated runs agree. - The browser leg passes, headless: 4 of 4 per-gate and 4 of 4 tile certificates, every vector under both, and every reject refused. - The checker passes, and `spirv-val` accepts both shaders. Planted non-uniform barriers fail the checker, and the browser rejects them too. - wai-quantum release and `wasm32-wasip2` gates pass, and the package carries both shaders. Timing is printed and never asserted, from a loaded debug build. The tiled kernel is much faster on long circuits over small registers and faster at 16 and 22 qubits on random circuits. It is slower on some 16- and 20-qubit circuits, so placement should rest on measured energy, which a later stage attaches. wai-quantum 0.4.2 is additive: default features, `full`, the per-gate implementation and every existing pin are unchanged. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wai-web gains the feature `quantum_webgpu` (wai/quantum_fabric,
wai/quantum_fabric_webgpu and wasm-bindgen-futures 0.4.77), which compiles
src/quantum_webgpu.rs on wasm32: quantum_webgpu_discover() opens the page's
adapter as a fabric member and probes its arithmetic; QuantumWebGpu reports the
member and returns promises for certify (random-v1 or fabric-v1, as certificate
JSON) and statevector_hash_hex (rejections read `<stage> (<class>): <reason>`).
The default build is unchanged: its bundle has the same size, and differs only
in the five panic-location line numbers of src/lib.rs that the new module
declaration shifts. wai-web/Cargo.lock only adds entries (web-sys resolves to
0.3.104 beside js-sys 0.3.104 and wasm-bindgen 0.2.127); indexmap's hashbrown
entry is relabelled with its version.

demo/quantum_webgpu.html runs demo/quantum_webgpu_worker.js, which certifies
both gatesets on random-v1 and fabric-v1, runs every quantum vector, offers
every reject file and a circuit one qubit wider than the member holds; a second
worker repeats the width check with the adapter offering the WebGPU default
buffer limits, where the member holds 25 qubits and refuses 26 as too-wide.
The bundle builds into demo/pkg-webgpu (ignored, as demo/pkg is).

quantum-conformance/browser_webgpu.mjs drives the page headless and holds every
result to the committed corpus and to fabric/expected.json (the four pinned
digests and the implementation id). On macOS a headless page is offered a
hardware adapter only with --use-angle=metal, which the script passes.

CI: the checks job builds the bundle; a new browser job on macos checks that
fabric/expected.json's pins are the crate's, builds the bundle with the
wasm-bindgen CLI 0.2.127, installs Playwright 1.60.0 and its headless browser
into the job's temp directory, and requires a GPU member certified bit-exact
4 of 4.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
plan (PLAN_REVISION 2): Config { tile_bits, run_max, skip_identity } with
PER_GATE (what compile makes) and TILED (tiles of up to 1024 amplitudes, runs
of up to 1024 gates, capped at 2^30 / 2^n, 16 at 26 qubits); Step::Tile
{ first, count, size }; compile_with, schedule, execute_from,
PlannedGate::{new, is_identity}. A tile run is a maximal run of consecutive
gates that act inside a tile: diagonal gates on any target, others with their
target's bit below the tile size. Only exact identities are skipped (circuit/1's
P(19..=32) is 2^30 - 1 and stays). The CPU executor mirrors the tile kernel's
index arithmetic tile by tile; lanes::exact_row is shared with apply_words.
Tests: every configuration (10 of them) equals the native simulator over
random-v1 (300) and fabric-v1 and certifies with the four lanes32 pins; long
random circuits and full-range random words tile exactly as they run gate by
gate; fabric-v1 covers every tile path; malformed programs are refused;
a_rounded_product_is_never_negated runs under every configuration.

wgsl: TILE_SHADER = lanes32.wgsl followed by lanes32_tile.wgsl (entry point
apply_tile, workgroup 256, one workgroup per tile, the tile size, gate range
and grid in a 32-byte uniform run record, gates in a 48-byte read-only storage
record), tile_implementation_id() = wgsl-lanes32-tile/de6fb388454a0144 (pinned),
tile records, tile_grid (x at most 8), TILE_MUTANTS T1 (barrier between gates
removed, detector Race over race_witness), T2 (the report's minimum dropped,
Range) and T3 (tbit < size -> <=, Malformed over malformed_tile_run).

Deviation from the plan's kernel text: no workgroup memory. naga 30.0.1
decorates every array type with ArrayStride and struct members with Offset,
and spirv-val (SPIRV-Tools 2026.4) rejects explicit layouts on Workgroup
variables (VUID-StandaloneSpirv-None-10684); per the plan the kernel was
restructured instead: each workgroup updates its own tile in place in the state
planes, with storageBarrier() between gates. The uniformity argument is
unchanged (one barrier, at the top level of the loop over the run's gates).
The min of the report is taken once per gate so that T2's text occurs once; a
tile outside the state reports bit 2; a record outside the gate buffer reads
as malformed.

Checker: SHADER and TILE_SHADER each under their own rules (entry points and
workgroup sizes, bindings, no workgroup memory, bool/u32 only, literal shifts,
atomics, barrier kind and placement at the top level of the body or of a
top-level for loop with no break, continue or return), translation to MSL,
SPIR-V and HLSL, mutants; lanes32_tile.spv for spirv-val; --tile-shader.

Member: WebGpuMember::tiled() compiles TILE_SHADER, probes it, plans with
TILED and reports the tile implementation; per-gate stays the default and the
fallback. Runs interleave gate and tile dispatches per submission (a second
uniform ring for run records, a storage buffer for the runs' gates), within
SUBMIT_BUDGET. Device tests: g8 kills T1-T3 on the tile member; new g12 runs
2,000 full-range cases tiled vs per-gate vs the CPU, the four pinned
certificates under the tile implementation, forced geometries, the conformance
vectors, five repeated runs, and prints per-gate vs tiled timing. The member
script requires 13 tests and both implementations' certificates and mutants.

Browser: QuantumWebGpu.tiled(); the worker certifies and runs the vectors on
the tile member too; browser_webgpu.mjs requires "tiles certified bit-exact 4
of 4" and fabric/expected.json pins tile_implementation.

CI: checks validate lanes32_tile.spv and package lanes32_tile.wgsl; the
browser job checks the tile pin and greps the tile certificates; the wasip2 job
requires the new tests by name, and its timeout is 90 minutes (the planner test
runs several minutes alone in a debug wasm build).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
From an independent review of the tile implementation (no input gave wrong
bits); each finding fixed:

- expected.json is authoritative: tests/fabric_expected.rs (excluded from the
  package, so publishing is unaffected; cargo drops its target and verification
  builds the library only) requires quantum-conformance/fabric/expected.json to
  be exactly the four certificates of the pinned corpora (gateset, corpus, seed,
  cases, wide circuits, the digest the CPU computes) and both implementation ids;
  a missing file fails. The browser job runs it in place of the grep step, whose
  comment now says what is checked. A copy giving fabric-v1 random-v1's digests
  fails it.
- The checker's step 6 adds a uniformity taint check: a barrier must not sit
  under a condition, or after a return, break or continue under a condition,
  that depends on the invocation's ids or on memory other invocations write
  (read-write storage, workgroup memory, atomics), directly or through a
  variable or a call. Uniform buffers, read-only storage, buffer lengths and
  the workgroup id, count and size are uniform. Planted early returns on
  local_invocation_index (the reviewer's variant among them), on a read-write
  load, through a variable and through a call each fail; the real shaders pass.
- The tile kernel refuses a run record (bit 4, nothing touched) unless its
  tiles cover each state plane exactly and its grid (num_workgroups, one deep)
  holds every tile once with x_groups to a row. The tile implementation id is
  now wgsl-lanes32-tile/54e443520c020013, re-pinned in wgsl.rs and
  expected.json.
- PlannedGate::is_well_formed: diag must match the words and the op index must
  not be u32::MAX (the tile kernel's "no op"). plan::execute_from returns None
  and the member refuses Internal(4) otherwise; compile never makes either.
- The member splits a tile step into dispatches of at most SUBMIT_BUDGET
  pair-updates (splitting a run is exact), so every submission keeps the budget
  the module and POLL_TIMEOUT documents state.
- g12 adds, for the tile member: starts other than zero; a first out-of-range
  op in a later submission with circuit/1 P(19) as filler (never skipped); a
  600-gate run at 20 qubits that the member splits; ill-formed planned gates
  refused by both implementations; and, inside WAI_QUANTUM_WEBGPU_WIDE only,
  the widest sizes (run cap 16, grid (8, 8192) at 26 qubits), which the member
  script then requires.
- Placement's tie order is documented as it is: with both implementations'
  entries for one member and no energy figures, place picks the tile
  implementation (its name sorts first); discovery opens per-gate members,
  which are the fallback. A test pins the order.
- wai/wai-web/src/lib.rs is in the workflow's paths; the wasip2 job requires
  the new unit test by name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum 0.4.2: tile runs and the browser member, as their own release
Some checks failed
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m29s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 5s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 17m20s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 4s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m8s
wai-pack / wai-pack · linux (pull_request) Successful in 1m30s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 17s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m24s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 4m2s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m42s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m8s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 6m40s
standards workspaces build + test / aac-rs (pull_request) Successful in 3m38s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 2m0s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 1m33s
standards workspaces build + test / map-rs (pull_request) Successful in 49s
standards workspaces build + test / mesh-rs (pull_request) Successful in 48s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Failing after 7m44s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 24s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 2m31s
standards workspaces build + test / openpay (pull_request) Failing after 15m34s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m40s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 5m25s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m52s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 28m19s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 42s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 5m46s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 3m44s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 2m13s
wai-pack / wai-pack · macos (pull_request) Failing after 44s
738a2cf3cb
0.4.1 is published, so the tile implementation and the browser member go out as
0.4.2, additive. Their CHANGELOG entries move out of the 0.4.1 entry into one of
their own; the version line changes in the six lockfiles that resolve
wai-quantum.

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!225
No description provided.