wai-quantum: the state vector resident on the GPU, auto placed by committed measurements, Pauli propagation on CUDA, an aarch64 board measured and in CI #558

Merged
dcharlot merged 10 commits from wai/quantum-gpu-resident into main 2026-11-01 01:56:12 -04:00
Owner

The GPU members now keep the state vector on the device for a whole plan, and auto places a state on one only where committed measurements show it is faster. Pauli propagation runs on CUDA with the CPU's term set, order and bits. Every placed answer is still the CPU's byte for byte. A third host, an aarch64 board with an integrated GPU, is measured, and the CUDA, OpenCL and WebGPU hardware jobs now run on it too.

What it adds

The resident state vector (sv64 host layout 2, for CUDA, HIP and OpenCL)

  • The state is zeroed on the device, or uploaded once for a partition. Every launch's parameters go up once as a LaunchTable. Each launch is enqueued in the CPU's order on one stream or in-order queue with no wait between them, and the state comes back once.
  • On CUDA the read-back uses two page-locked buffers in turn, which cut a 1 GiB read-back from 760 to about 440 ms.
  • A gate on two to five qubits takes a kernel compiled for its group size, its group held in registers.
  • The host layout is part of every implementation id, so each certificate is of the resident path. Splitting is unchanged: exchanges are its only host round trips.
  • run_from_zero is new, and max_qubits follows the device's memory (at most 32) rather than the dyadic core's 26.

The wide kernels certified on their own. The ground-truth random-circuit test found that one host's OpenCL CPU runtime reorders the sums of the 5-qubit kernel on about one input in six. The pinned corpus (fusion 0, 2 and 3) never reaches that kernel, so the kernels for gates on more than three qubits now carry their own certificate (KERNEL_SV_RUN_WIDE, corpus SV_WIDE: 120 cases, pinned at 093614f2…; a 36-case version let the fault through). A member is placed for a job with such gates only if this certificate is bit-exact. Otherwise that job is refused with J013, and accelerator skips the member. The receipt and wai-quantum fabric show each member's wide certificate.

auto by measurement (engine::fabric_measured)

  • The resident state vector was timed against the CPU as whole jobs of one circuit family (ten layers of arbitrary-angle rotations and brickwork entangling pairs, 1000 shots): five runs of each, alternating, at fusion 3, 2 and 1, from 18 qubits to as many as half the host's memory held. The rows, each host and its load, and what discovering its members took are committed; examples/fabric_timing.rs takes them again.
  • A row is faster when every run on the member beat every run on the CPU. auto places a state on a member from the smallest measured size from which every row up to the largest is faster, up to that largest, at the fusion width measured, for a plan of at least the measured launches. A process that has not discovered its members does so only where the saving also covers that discovery.
  • Hosts are named by fabric::host (the processor's model, architecture and threads, hashed), members by id, so another processor, device or driver matches nothing and stays on the CPU.
  • A job auto placed carries the rule, the host, the day, the load, the discovery and the measured row it rests on in its receipt (fabric.auto, with fabric_spread), and the plan's reasons say so. wai-quantum fabric prints the rule, where it places on this host, and every measured row (auto_rule).
  • A member auto chose that fails during the run hands the job back to the CPU.

What the rows say (medians):

host fusion qubits CPU CUDA HIP OpenCL auto
a laptop: 20-thread CPU, GPU of compute capability 8.9 3 22 207 ms 228 ms 223 ms CPU
3 26 3.74 s 4.44 s 4.29 s CPU
32-thread CPU sharing 128 GiB with its integrated GPU (gfx1151) 3 22 205 ms 108 ms 102 ms GPU
3 28 9.81 s 8.47 s 8.14 s GPU
1 24 1.51 s 2.30 s 2.39 s CPU
14-core aarch64 CPU sharing 122 GiB with its integrated GPU (compute capability 11.0) 3 22 575 ms 366 ms 363 ms CPU
3 28 22.5 s 27.7 s 27.7 s CPU
  • The laptop's GPU is never faster from 22 qubits up, at any fusion width (its binary64 rate, not the copies, bounds it), so auto keeps every state on its CPU.
  • The integrated GPU beat its CPU in every run at fusion 3 and 2 from 18 to 28 qubits, and lost from 22 qubits at fusion 1, so auto places fused jobs there. Discovery takes 1.2 s, more than the saving below 28 qubits, so a process whose members are discovered (a service, a session, or a job that asked for one) places from 18 qubits, and a fresh one only at 28.
  • On the aarch64 board, the GPU by CUDA and by OpenCL alike won some rows below 24 qubits and lost every row from 26 qubits at every fusion width, so no size has a faster tail and auto keeps its states on the CPU.

Pauli propagation on the GPU (quantum_spd::device)

  • The device runs each step's arithmetic on every term at once: one multiply by sin, one by cos, one addition where a new term meets an old one, and sign changes, built without fused multiply-add.
  • The host keeps everything that decides the term set. A rotation's new terms are found in the table, dead slots included, and the rest are appended in the order of the terms they came from by an order-preserving prefix sum. The host compacts exactly when the CPU does. A truncation hands back the magnitudes it dropped in table order, and the bound and the value are added on the host in table order.
  • HostDevice runs that protocol on the CPU, one term after another, and is the CPU's propagation bit for bit, final table included, over 100 cases at one thread and many.
  • The CUDA member (CudaSpd, kernels quantum_fabric::spd) is certified on SPD_DEFAULT: 40 cases, each case's output every value, bound, threshold reached and count, and the final table slot for slot. A mutant that appended a revived string again survived a corpus of numbers alone; with the table, all six mutants are killed.
  • A pauli job with fabric set to accelerator, or a member's id or key, runs its sum of terms on that member, with the CPU's answers byte for byte. cpu names the CPU, and a split is refused (J013). auto keeps the method on the CPU, since no member is measured faster for it.

The OpenCL member on PoCL. Found certifying PoCL 7.2 on the aarch64 board, two things PoCL's CUDA device does not do, each of which ended the process rather than returning an error:

  • a fill of 8 bytes or more, which the resident state vector used to zero its arrays with 0.0. A fill of a 4-byte zero gives the same bits and every runtime takes it, so the arrays are zeroed so on every device;
  • a launch of the wider kernels at the work-group size it chooses itself (CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES). On PoCL's GPU devices alone, each launch names the largest power of two the kernel takes there, at most 128, with the global size rounded up to a multiple of it; the kernels stride, so the items past the work do nothing. Every other runtime still chooses its own, so their measured timings stand.

Surfaces. CLI help and the fabric command's text, both MCP servers' descriptions, the guide, the schemas (new optional fabric_wide, fabric_auto, fabric_spread, auto_rule and pauli), the engine and CLI READMEs (en and zh-CN) and the changelog. The agent docs are re-blessed.

CI

  • The placement legs hold auto to the measurements on each host. The CUDA leg certifies the propagation and kills its mutants. The placement legs run a pauli job on each member, or see accelerator refused. The container legs name the protocol's tests.
  • cuda-hw, opencl-hw and the WebGPU member-hw jobs become matrices over the hosts with such devices, each host's devices declared in the workflow: jetson-hub (linux-hw), as before, and the aarch64 board (linux-arm64-hw), new: gpu-cuda-sm110 on CUDA 13.2; OpenCL through PoCL 7.2 for its CPU (opencl-cpu-13b5) and, through CUDA, its GPU (opencl-gpu-10de, with POCL_CUDA_GPU_ARCH=sm_90, since PoCL's LLVM 18 has no sm_110); and Vulkan for its GPU and the software driver. jetson-hub's jobs keep their names except the WebGPU one, which is now member · GPU, Vulkan (jetson-hub).

Checked

Where Result
jetson-hub (laptop GPU) The workflow jobs replayed step by step: cuda-hw 6/6 (cuda_sv 4, cuda_spd 3, fabric_placement 7), opencl-hw 3/3 (opencl_sv 4), hip-absent every step. With the PoCL change: its OpenCL members certify as before (its GPU bit-exact on both corpora, its CPU's wide kernels not bit-exact, its integrated GPU refused for want of binary64).
framework-128gb (integrated GPU) hip-hw 5/5 (hip_sv 4, fabric_placement 7), opencl-amd 3/3, cuda-absent 2/2.
The aarch64 board (GPU of compute capability 11.0) cuda-hw 6/6: the CUDA member's 4 certificates and the tensor-core kernel's 4, bit-exact with the pinned digests the laptop's GPU gives; the state vector's pinned 48 and 120 cases; 140 random resident runs; 4 of 4 mutants; Pauli propagation's certificate and large sums. opencl-hw: PoCL's CPU and GPU members each certify bit-exact, wide certificate included, their 48 and 120 cases and 140 random runs the CPU's bit for bit. The fabric's 7 placement tests, one state split over the CUDA GPU, PoCL's GPU, PoCL's CPU and the CPU reference giving the CPU's answers byte for byte. The WebGPU member-hw job: the member's 14 tests (the wide sizes to 26 qubits on the GPU) and the conformance corpus through the member. examples/fabric_timing.rs: 36 rows.
macOS build host opencl_sv: 4 passed (the no-binary64 refusal). Under --features full: 53 lib tests and 7 fabric_placement. Both pins reproduce on arm64.
Linux build host wasm32-wasip2 under wasmtime 49.0.2: quantum_fabric:: 117 passed. Repository checks: every line ok. Breaking gate: no unacknowledged break. CLI smoke: every check held. check_schemas.py with the pinned jsonschema: exit 0. Service mcp::: 14 passed, then the CLI's tests.
After rebasing onto the scale-out PR (Linux build host) build-views --check: the committed page is built from its 49 sources. Service mcp::: 14 passed. CLI: 72 passed (bin) and every integration target; clippy with cuda,hip,opencl clean. Service: 37 + 1. wai-web native: 29 passed. Engine --features full: 1162 lib tests passed and every test binary (engine::fabric 6, engine::fabric_measured 4, quantum_spd 23, quantum_fabric 117). The contract feature sets' fabric and schema tests: 12 and 11 passed. CLI and service smoke: every check held. Breaking gate: no unacknowledged break. Repository checks: all 10 lines ok. After the board's rows: engine::fabric and engine::fabric_measured under --features full, 10 passed.
After rebasing onto the scale-out PR (macOS build host) check_schemas.py: 47 tool answers follow their schemas.
On main de3fce0d3 with the variational and scale-out PRs under it (macOS build host) wai-web wasm32: 21 passed. The Studio's browserless tests passed (hardware, telemetry, update, locales). build-views --check passed. studio.test.mjs run twice: every check passed.
The same tree (Linux build host) The views page rebuilt with wai/wai-quantum-views/build.sh in catthehacker/ubuntu:act-22.04 with Rust 1.98.0: the same bytes as the committed page, so the engine changes since it was built (the board's rows, the PoCL fix) do not reach it.
On main 2820e1e57 Rebased onto main: under it now are the scale-out PR as merged, with its fix to the getting-started notebook's stored method list (bda065b52), and the receipt ledger's billing by the work spent (#556), in wai-rs, wai-ledger and their specifications. Every changed line is main's, and no crate this PR changes compiles #556's code (none depends on wai-rs), so nothing was run again by hand for it; this PR's CI runs every leg. wai-quantum-py/ci/check.sh passed on a tree holding this PR and the registry PR above it (78 passed, 5 of 5 notebooks ran and matched).
After this PR's CI (macOS build host) The pulse, error-correction and variational workflows check the library on quantum_engine and the sets beside it under -D warnings, where expectation_table was dead: only the device reference (under quantum_fabric) and a test read it. d4cdbb7da compiles it only there. Every cargo check line of the three workflows, and quantum_lang, quantum_toolchain, quantum_fabric and full --lib --tests, now compiles clean under -D warnings. quantum_spd tests: 28 passed under full, 22 under the contract set.

The views page was rebuilt (ae432098e) with wai/wai-quantum-views/build.sh in catthehacker/ubuntu:act-22.04 with Rust 1.98.0, as the views workflow rebuilds it, since the engine inside it changed.

Not done

  • Pauli propagation on HIP and OpenCL: only CUDA has a member for it.
  • auto for pauli: no member is measured for it, so it stays on the CPU.
  • The opencl-absent leg was not replayed (every host here has OpenCL); its only change is its pass count, from 3 to 4.

Breaking

None. The breaking gate passes, and the changelog acknowledges no break.

🤖 Generated with Claude Code

The GPU members now keep the state vector on the device for a whole plan, and `auto` places a state on one only where committed measurements show it is faster. Pauli propagation runs on CUDA with the CPU's term set, order and bits. Every placed answer is still the CPU's byte for byte. A third host, an aarch64 board with an integrated GPU, is measured, and the CUDA, OpenCL and WebGPU hardware jobs now run on it too. ## What it adds **The resident state vector** (sv64 host layout 2, for CUDA, HIP and OpenCL) - The state is zeroed on the device, or uploaded once for a partition. Every launch's parameters go up once as a `LaunchTable`. Each launch is enqueued in the CPU's order on one stream or in-order queue with no wait between them, and the state comes back once. - On CUDA the read-back uses two page-locked buffers in turn, which cut a 1 GiB read-back from 760 to about 440 ms. - A gate on two to five qubits takes a kernel compiled for its group size, its group held in registers. - The host layout is part of every implementation id, so each certificate is of the resident path. Splitting is unchanged: exchanges are its only host round trips. - `run_from_zero` is new, and `max_qubits` follows the device's memory (at most 32) rather than the dyadic core's 26. **The wide kernels certified on their own.** The ground-truth random-circuit test found that one host's OpenCL CPU runtime reorders the sums of the 5-qubit kernel on about one input in six. The pinned corpus (fusion 0, 2 and 3) never reaches that kernel, so the kernels for gates on more than three qubits now carry their own certificate (`KERNEL_SV_RUN_WIDE`, corpus `SV_WIDE`: 120 cases, pinned at `093614f2…`; a 36-case version let the fault through). A member is placed for a job with such gates only if this certificate is bit-exact. Otherwise that job is refused with `J013`, and `accelerator` skips the member. The receipt and `wai-quantum fabric` show each member's wide certificate. **`auto` by measurement** (`engine::fabric_measured`) - The resident state vector was timed against the CPU as whole jobs of one circuit family (ten layers of arbitrary-angle rotations and brickwork entangling pairs, 1000 shots): five runs of each, alternating, at fusion 3, 2 and 1, from 18 qubits to as many as half the host's memory held. The rows, each host and its load, and what discovering its members took are committed; `examples/fabric_timing.rs` takes them again. - A row is faster when every run on the member beat every run on the CPU. `auto` places a state on a member from the smallest measured size from which every row up to the largest is faster, up to that largest, at the fusion width measured, for a plan of at least the measured launches. A process that has not discovered its members does so only where the saving also covers that discovery. - Hosts are named by `fabric::host` (the processor's model, architecture and threads, hashed), members by id, so another processor, device or driver matches nothing and stays on the CPU. - A job `auto` placed carries the rule, the host, the day, the load, the discovery and the measured row it rests on in its receipt (`fabric.auto`, with `fabric_spread`), and the plan's reasons say so. `wai-quantum fabric` prints the rule, where it places on this host, and every measured row (`auto_rule`). - A member `auto` chose that fails during the run hands the job back to the CPU. What the rows say (medians): | host | fusion | qubits | CPU | CUDA | HIP | OpenCL | auto | |---|---|---|---|---|---|---|---| | a laptop: 20-thread CPU, GPU of compute capability 8.9 | 3 | 22 | 207 ms | 228 ms | | 223 ms | CPU | | | 3 | 26 | 3.74 s | 4.44 s | | 4.29 s | CPU | | 32-thread CPU sharing 128 GiB with its integrated GPU (gfx1151) | 3 | 22 | 205 ms | | 108 ms | 102 ms | GPU | | | 3 | 28 | 9.81 s | | 8.47 s | 8.14 s | GPU | | | 1 | 24 | 1.51 s | | 2.30 s | 2.39 s | CPU | | 14-core aarch64 CPU sharing 122 GiB with its integrated GPU (compute capability 11.0) | 3 | 22 | 575 ms | 366 ms | | 363 ms | CPU | | | 3 | 28 | 22.5 s | 27.7 s | | 27.7 s | CPU | - The laptop's GPU is never faster from 22 qubits up, at any fusion width (its binary64 rate, not the copies, bounds it), so `auto` keeps every state on its CPU. - The integrated GPU beat its CPU in every run at fusion 3 and 2 from 18 to 28 qubits, and lost from 22 qubits at fusion 1, so `auto` places fused jobs there. Discovery takes 1.2 s, more than the saving below 28 qubits, so a process whose members are discovered (a service, a session, or a job that asked for one) places from 18 qubits, and a fresh one only at 28. - On the aarch64 board, the GPU by CUDA and by OpenCL alike won some rows below 24 qubits and lost every row from 26 qubits at every fusion width, so no size has a faster tail and `auto` keeps its states on the CPU. **Pauli propagation on the GPU** (`quantum_spd::device`) - The device runs each step's arithmetic on every term at once: one multiply by sin, one by cos, one addition where a new term meets an old one, and sign changes, built without fused multiply-add. - The host keeps everything that decides the term set. A rotation's new terms are found in the table, dead slots included, and the rest are appended in the order of the terms they came from by an order-preserving prefix sum. The host compacts exactly when the CPU does. A truncation hands back the magnitudes it dropped in table order, and the bound and the value are added on the host in table order. - `HostDevice` runs that protocol on the CPU, one term after another, and is the CPU's propagation bit for bit, final table included, over 100 cases at one thread and many. - The CUDA member (`CudaSpd`, kernels `quantum_fabric::spd`) is certified on `SPD_DEFAULT`: 40 cases, each case's output every value, bound, threshold reached and count, and the final table slot for slot. A mutant that appended a revived string again survived a corpus of numbers alone; with the table, all six mutants are killed. - A `pauli` job with `fabric` set to `accelerator`, or a member's id or key, runs its sum of terms on that member, with the CPU's answers byte for byte. `cpu` names the CPU, and a split is refused (`J013`). `auto` keeps the method on the CPU, since no member is measured faster for it. **The OpenCL member on PoCL.** Found certifying PoCL 7.2 on the aarch64 board, two things PoCL's CUDA device does not do, each of which ended the process rather than returning an error: - a fill of 8 bytes or more, which the resident state vector used to zero its arrays with 0.0. A fill of a 4-byte zero gives the same bits and every runtime takes it, so the arrays are zeroed so on every device; - a launch of the wider kernels at the work-group size it chooses itself (`CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES`). On PoCL's GPU devices alone, each launch names the largest power of two the kernel takes there, at most 128, with the global size rounded up to a multiple of it; the kernels stride, so the items past the work do nothing. Every other runtime still chooses its own, so their measured timings stand. **Surfaces.** CLI help and the `fabric` command's text, both MCP servers' descriptions, the guide, the schemas (new optional `fabric_wide`, `fabric_auto`, `fabric_spread`, `auto_rule` and `pauli`), the engine and CLI READMEs (en and zh-CN) and the changelog. The agent docs are re-blessed. **CI** - The placement legs hold `auto` to the measurements on each host. The CUDA leg certifies the propagation and kills its mutants. The placement legs run a `pauli` job on each member, or see `accelerator` refused. The container legs name the protocol's tests. - `cuda-hw`, `opencl-hw` and the WebGPU `member-hw` jobs become matrices over the hosts with such devices, each host's devices declared in the workflow: jetson-hub (`linux-hw`), as before, and the aarch64 board (`linux-arm64-hw`), new: `gpu-cuda-sm110` on CUDA 13.2; OpenCL through PoCL 7.2 for its CPU (`opencl-cpu-13b5`) and, through CUDA, its GPU (`opencl-gpu-10de`, with `POCL_CUDA_GPU_ARCH=sm_90`, since PoCL's LLVM 18 has no sm_110); and Vulkan for its GPU and the software driver. jetson-hub's jobs keep their names except the WebGPU one, which is now `member · GPU, Vulkan (jetson-hub)`. ## Checked | Where | Result | |---|---| | jetson-hub (laptop GPU) | The workflow jobs replayed step by step: `cuda-hw` 6/6 (`cuda_sv` 4, `cuda_spd` 3, `fabric_placement` 7), `opencl-hw` 3/3 (`opencl_sv` 4), `hip-absent` every step. With the PoCL change: its OpenCL members certify as before (its GPU bit-exact on both corpora, its CPU's wide kernels not bit-exact, its integrated GPU refused for want of binary64). | | framework-128gb (integrated GPU) | `hip-hw` 5/5 (`hip_sv` 4, `fabric_placement` 7), `opencl-amd` 3/3, `cuda-absent` 2/2. | | The aarch64 board (GPU of compute capability 11.0) | `cuda-hw` 6/6: the CUDA member's 4 certificates and the tensor-core kernel's 4, bit-exact with the pinned digests the laptop's GPU gives; the state vector's pinned 48 and 120 cases; 140 random resident runs; 4 of 4 mutants; Pauli propagation's certificate and large sums. `opencl-hw`: PoCL's CPU and GPU members each certify bit-exact, wide certificate included, their 48 and 120 cases and 140 random runs the CPU's bit for bit. The fabric's 7 placement tests, one state split over the CUDA GPU, PoCL's GPU, PoCL's CPU and the CPU reference giving the CPU's answers byte for byte. The WebGPU `member-hw` job: the member's 14 tests (the wide sizes to 26 qubits on the GPU) and the conformance corpus through the member. `examples/fabric_timing.rs`: 36 rows. | | macOS build host | `opencl_sv`: 4 passed (the no-binary64 refusal). Under `--features full`: 53 lib tests and 7 `fabric_placement`. Both pins reproduce on arm64. | | Linux build host | wasm32-wasip2 under wasmtime 49.0.2: `quantum_fabric::` 117 passed. Repository checks: every line ok. Breaking gate: no unacknowledged break. CLI smoke: every check held. `check_schemas.py` with the pinned `jsonschema`: exit 0. Service `mcp::`: 14 passed, then the CLI's tests. | | After rebasing onto the scale-out PR (Linux build host) | `build-views --check`: the committed page is built from its 49 sources. Service `mcp::`: 14 passed. CLI: 72 passed (bin) and every integration target; clippy with `cuda,hip,opencl` clean. Service: 37 + 1. wai-web native: 29 passed. Engine `--features full`: 1162 lib tests passed and every test binary (`engine::fabric` 6, `engine::fabric_measured` 4, `quantum_spd` 23, `quantum_fabric` 117). The contract feature sets' fabric and schema tests: 12 and 11 passed. CLI and service smoke: every check held. Breaking gate: no unacknowledged break. Repository checks: all 10 lines ok. After the board's rows: `engine::fabric` and `engine::fabric_measured` under `--features full`, 10 passed. | | After rebasing onto the scale-out PR (macOS build host) | `check_schemas.py`: 47 tool answers follow their schemas. | | On main `de3fce0d3` with the variational and scale-out PRs under it (macOS build host) | wai-web wasm32: 21 passed. The Studio's browserless tests passed (hardware, telemetry, update, locales). `build-views --check` passed. `studio.test.mjs` run twice: every check passed. | | The same tree (Linux build host) | The views page rebuilt with `wai/wai-quantum-views/build.sh` in `catthehacker/ubuntu:act-22.04` with Rust 1.98.0: the same bytes as the committed page, so the engine changes since it was built (the board's rows, the PoCL fix) do not reach it. | | On main `2820e1e57` | Rebased onto main: under it now are the scale-out PR as merged, with its fix to the getting-started notebook's stored method list (`bda065b52`), and the receipt ledger's billing by the work spent (#556), in `wai-rs`, `wai-ledger` and their specifications. Every changed line is main's, and no crate this PR changes compiles #556's code (none depends on `wai-rs`), so nothing was run again by hand for it; this PR's CI runs every leg. `wai-quantum-py/ci/check.sh` passed on a tree holding this PR and the registry PR above it (78 passed, 5 of 5 notebooks ran and matched). | | After this PR's CI (macOS build host) | The pulse, error-correction and variational workflows check the library on `quantum_engine` and the sets beside it under `-D warnings`, where `expectation_table` was dead: only the device reference (under `quantum_fabric`) and a test read it. `d4cdbb7da` compiles it only there. Every `cargo check` line of the three workflows, and `quantum_lang`, `quantum_toolchain`, `quantum_fabric` and `full --lib --tests`, now compiles clean under `-D warnings`. `quantum_spd` tests: 28 passed under full, 22 under the contract set. | **The views page** was rebuilt (`ae432098e`) with `wai/wai-quantum-views/build.sh` in `catthehacker/ubuntu:act-22.04` with Rust 1.98.0, as the views workflow rebuilds it, since the engine inside it changed. ## Not done - Pauli propagation on HIP and OpenCL: only CUDA has a member for it. - `auto` for `pauli`: no member is measured for it, so it stays on the CPU. - The `opencl-absent` leg was not replayed (every host here has OpenCL); its only change is its pass count, from 3 to 4. ## Breaking None. The breaking gate passes, and the changelog acknowledges no break. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A member now runs a whole plan with the state on the device (sv64 host
layout 2): the state is zeroed there, or uploaded once for a partition, every
launch's parameters go up once as a LaunchTable, each launch is enqueued in the
CPU's order on one stream or in-order queue with no wait between them, and
the state comes back once, on CUDA through two page-locked buffers in turn.
The formulas are the same; a gate on two to five qubits takes a kernel
compiled for its group size, its group in registers. The host layout is part
of every implementation id, so each certificate is of the resident path.
Splitting is unchanged: exchanges are its only host round trips. CudaSv,
HipSv and OpenClSv gain run_from_zero, and max_qubits follows the device's
memory (at most 32) rather than the dyadic core's 26.

Ground truth first: the uploaded table decodes to the plan and replays to the
CPU's state; on each device random circuits at fusion 0 to 6, from |0...0> and
from a given state, are the CPU's bit for bit. That test caught one CPU's
OpenCL runtime reordering the five-qubit kernel's sums on about one input in
six, which the pinned corpus (fusion 0, 2 and 3) never reaches. So the wider
kernels (gates on more than three qubits at once) are certified on a corpus of
their own, SV_WIDE (120 cases at fusion 4 to 6, pinned; 36 cases let the fault
through), and a member is placed for such a job only where they certified
bit-exact; elsewhere it is refused (J013) and accelerator passes over it. The
receipt carries the member's wide certificate when the job used those kernels
(fabric_wide, optional, in the job-result and tools schemas).

On the fleet: the laptop GPU by CUDA and OpenCL, the CPU beside it by OpenCL,
and the integrated GPU by HIP and OpenCL each certified, random circuits and
placed jobs the CPU's bits, the split included; the CPU's OpenCL wider kernels
do not certify and it runs only narrow ones. CI legs updated (four
state-vector tests, six placement tests, the wide certificate checked); agent
docs re-blessed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The resident state vector was timed against the CPU as whole jobs of one
circuit family (ten layers of arbitrary-angle rotations and brickwork
entangling pairs, 1000 shots): five runs of each, alternating, at fusion 3, 2
and 1, from 18 qubits to as many as half the host's memory held, on two hosts
on 2026-10-30. The rows, each host's load, and what discovering its members
took are committed (engine::fabric_measured), and examples/fabric_timing.rs
takes them again.

A row is faster when every run on the member beat every run on the CPU. auto
places a state on a member from the smallest measured size from which every
row up to the largest is faster, up to that largest, at the fusion width
measured, for a plan of at least the measured launches; a process that has not
discovered its members yet does so only where the saving also covers that
discovery. Hosts are named by fabric::host (the processor's model,
architecture and threads, hashed), members by id, so another processor,
device or driver matches nothing and stays on the CPU.

What the rows say: the laptop GPU is never faster from 22 qubits up at any
fusion width (26 qubits at fusion 3: 3.74 s on the CPU, 4.44 s by CUDA, 4.29 s
by OpenCL), so auto keeps every state on that CPU. The integrated GPU beat its
CPU in every run at fusion 3 and 2 from 18 to 28 qubits (22 qubits: 205 ms
against 108 ms by HIP and 102 ms by OpenCL; 28: 9.81 s against 8.47 s and
8.14 s) and lost from 22 qubits at fusion 1, so auto places fused jobs there,
from 18 qubits once the members are discovered and at 28 in a fresh process
(discovery takes 1.2 s). A member auto chose that fails during the run hands
the job back to the CPU.

A job auto placed carries the rule, the host, the day, the load and the
measured row it rests on in its receipt (fabric.auto, with fabric_spread), and
the plan's reasons say so; wai-quantum fabric prints the rule, where it places
here and every measured row (auto_rule). Docs, help, both MCP servers'
descriptions, the guide and the changelog say so; agent docs re-blessed. The
placement legs hold auto to the measurements on each host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The propagation's sum of terms can now live on a device (quantum_spd::device).
The device runs each step's arithmetic on every term at once: one multiply by
sin, one by cos, one addition where a new term meets an old one, and sign
changes, built without fused multiply-add. The host keeps everything that
decides the term set. A rotation's new terms are found in the table, dead
slots included, and the rest are appended in the order of the terms they came
from by an order-preserving prefix sum. The host counts dead and live terms
and compacts exactly when the CPU does. A truncation hands back the
magnitudes it dropped in table order, and the bound and the value are added
on the host in table order (expectation_on over an SpdDevice).

Ground truth first: HostDevice runs that protocol on the CPU, one term after
another, and is the CPU's propagation bit for bit, final table included, over
100 cases at one thread and many. The CUDA member (CudaSpd; kernels
quantum_fabric::spd, implementation cuda-spd) is certified on SPD_DEFAULT
against the CPU. The corpus is 40 cases on one to four words, with term and
weight caps, layered circuits whose dropped strings come back and are revived
in place, and one exact cancellation. Each case's output is every value,
bound, threshold reached and count, and the final table slot for slot, so a
device that keeps the numbers but not the term set or its order is caught: a
mutant that appended a revived string again survived a corpus of numbers
alone. All six mutants are killed. On the laptop GPU: certified, and 24
wider random cases up to two million terms and the 127-qubit kicked-Ising
circuit are the CPU's results bit for bit.

A pauli job asks with fabric (accelerator, or a member's id or key): its sum
of terms runs on that member, with the CPU's answers byte for byte, and the
result's fabric names the member and its certificate (kernel
wai.quantum.spd/propagate). cpu names the CPU, and a split is refused (J013).
auto keeps the method on the CPU, since no member is measured faster for it.
Its members are discovered apart from the state vector's, and wai-quantum
fabric lists them (pauli, optional, in the tools schema). The CUDA leg
certifies the propagation and kills its mutants; the placement legs run a
pauli job on each member, or see accelerator refused; the container legs
name the protocol's tests. Docs, help, both MCP servers' descriptions, the
guide and the changelog say so; agent docs re-blessed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Rebuilt with wai/wai-quantum-views/build.sh in catthehacker/ubuntu:act-22.04 with Rust 1.98.0, as
the views workflow rebuilds it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
engine::fabric_measured gains a host and its 18 rows, taken with examples/fabric_timing.rs on
2026-10-31: 14 aarch64 cores sharing 122 GiB with an integrated GPU of compute capability 11.0, by
CUDA. The GPU won some rows below 24 qubits and lost every row from 26 qubits at fusion 3, 2 and 1
(28 qubits, fusion 3: 22.6 s on the CPU against 27.7 s), so no size has a faster tail there and auto
keeps that host's states on the CPU. The README (en, zh-CN) and the changelog say so.

The same board ran the CUDA hardware job's steps: the member found as gpu-cuda-sm110 on CUDA 13.2,
every certificate bit-exact with the pinned digests the x86-64 laptop's GPU gives.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Two things PoCL's CUDA device does not do, each of which ended the process rather than returning
an error, found certifying it on an aarch64 board:
- a fill of 8 bytes or more (pocl-cuda.c: "fill_kernel with pattern_size >=8 is unimplemented"),
  which the resident state vector used to zero its arrays with 0.0. A fill of a 4-byte zero gives
  the same bits and every runtime takes it, so the arrays are zeroed so on every device;
- a launch of the wider kernels at the work-group size it chooses itself
  (CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES). On PoCL's GPU devices alone, each launch now names the
  largest power of two the kernel takes there (CL_KERNEL_WORK_GROUP_SIZE), at most 128, with the
  global size rounded up to a multiple of it; the kernels stride, so the items past the work do
  nothing. Every other runtime still chooses its own, so their measured timings stand.

On the board, PoCL 7.2 (LLVM 18) on its CPU (opencl-cpu-13b5) and on its GPU through CUDA
(opencl-gpu-10de) then certify bit-exact on every kernel: the lanes kernels, the state vector's
48 cases (b7d129d0) and its 120 wide cases (093614f2), 4 of 4 mutants caught, and 140 random
resident runs the CPU's bit for bit. The laptop's OpenCL members certify as before with the
change: its GPU bit-exact on both, its CPU's wide kernels not bit-exact, its integrated GPU
refused for want of binary64.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
cuda-hw and opencl-hw become matrices over the hosts with such devices. Each entry declares the
host's devices in the repository: its CUDA GPU, the fabric's members there and one state split
over them (cuda-hw), and its OpenCL devices (opencl-hw), sorted as discovery names them.
- jetson-hub (linux-hw), as before: gpu-cuda-sm89; fabric gpu-cuda-sm89, opencl-cpu-8086 and
  opencl-gpu-10de; OpenCL opencl-cpu-8086, opencl-gpu-10de and opencl-gpu-8086 (its integrated GPU,
  whose state vector is refused for want of binary64). Its jobs keep their names.
- thor (linux-arm64-hw), new: gpu-cuda-sm110 (compute capability 11.0, CUDA 13.2); fabric
  gpu-cuda-sm110, opencl-cpu-13b5 and opencl-gpu-10de; OpenCL through PoCL 7.2, for its CPU and,
  through CUDA, its GPU, with POCL_CUDA_GPU_ARCH=sm_90 (PoCL's LLVM 18 has no sm_110; the driver
  compiles the PTX for the GPU).
The one step line that named jetson-hub's CUDA key takes the declared one.

Every step of both jobs ran on thor before this: the CUDA member's 4 certificates, the tensor-core
kernel's 4, the state vector's pinned 48 and 120 cases, 140 random resident runs, 4 of 4 mutants,
Pauli propagation's certificate and large sums; the OpenCL members' 4 certificates each, their
state vectors' 48 and 120 cases and 140 random runs; and the fabric's 7 placement tests, one state
split over the CUDA GPU, PoCL's GPU, PoCL's CPU and the CPU reference giving the CPU's answers byte
for byte.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
member-hw is now a matrix over the two hosts with GPUs, the way the CUDA
and OpenCL hardware jobs are. Each host's adapters are declared in the
workflow: jetson-hub's discrete and integrated GPUs and the software
Vulkan driver (the set its runner declared until now), and the board's
GPU and the software Vulkan driver. Both steps ran on the board first:
the member's 14 tests, the wide sizes to 26 qubits on its GPU, and the
conformance corpus through the member.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum: the aarch64 board measured again, by CUDA and by OpenCL
Some checks failed
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (pull_request) Successful in 1m38s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Successful in 5m7s
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Successful in 26s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m30s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m17s
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Successful in 15s
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Successful in 29s
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Successful in 24s
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Successful in 27s
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Successful in 29s
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Successful in 16m10s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 2m29s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m26s
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Successful in 3m16s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 20m28s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 3m33s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m1s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Successful in 3m47s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 9m37s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 1m11s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 31s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 5m16s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 5m44s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 1m38s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 21s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 15m57s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Failing after 1m36s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m45s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 11m24s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h7m20s
cd7e5068d8
engine::fabric_measured's rows for the board are replaced by one pass over
both its GPU members, the way the other hosts were measured: 36 rows taken
with examples/fabric_timing.rs on 2026-10-31, now that the board has an
OpenCL driver (PoCL 7.2, whose CUDA device runs PTX for sm_90). From 22
qubits the OpenCL member's times are within 1.2% of the CUDA one's; at 20
qubits it is 21 to 36% quicker than CUDA. Both won some rows below 24
qubits and lost every row from 26 qubits at every fusion width (28 qubits,
fusion 3: 22.5 s on the CPU against 27.7 s by either), so no size has a
faster tail there and auto still keeps the board's states on the CPU. From
22 qubits the CUDA rows repeat the first pass's to within 2.5% (the GPU's
own times to within 0.5%). Discovering both members takes 2.6 s. The host
entry, the module notes, the README (en, zh-CN) and the changelog say so.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-quantum quantum_spd: expectation_table compiled only where it is called
All checks were successful
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Successful in 28s
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Successful in 11s
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Successful in 10s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (pull_request) Successful in 1m32s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m20s
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Successful in 19s
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Successful in 30s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m34s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 15m39s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m23s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (pull_request) Successful in 1m52s
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Successful in 15m25s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 58s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Successful in 8m9s
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Successful in 3m1s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 2m8s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 3m23s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Successful in 3m42s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 5m56s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 17m13s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 11m22s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 15m57s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 1m16s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 7m20s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 31s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 23s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Successful in 1m44s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Successful in 1m58s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m46s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h12m28s
d4cdbb7da2
Only the device reference (under `quantum_fabric`) and the host test read
the table. The pulse, error-correction and variational workflows check the
library on `quantum_engine` and the sets beside it under `-D warnings`,
where the function was dead and failed the build.

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