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]>
218 lines
15 KiB
YAML
218 lines
15 KiB
YAML
name: wai-quantum HIP member
|
|
|
|
# The fabric's HIP member: the CUDA member's kernel text, unchanged, compiled at
|
|
# run time by HIP's runtime compiler for each device's own architecture and
|
|
# certified bit-exact against the CPU reference's pinned corpus digests.
|
|
#
|
|
# - hip-hw (framework-128gb, its integrated GPU): the device tests must find
|
|
# exactly the declared device, agree with the CPU on the arithmetic probe and
|
|
# on random states (out-of-range runs included), reproduce all four pinned
|
|
# certificates (two gatesets, random-v1 and fabric-v1), and catch every
|
|
# kernel mutant; the matrix-instruction kernel, every product formed by the
|
|
# GPU's bf16 matrix instruction from offset-binary 8-bit limbs, must do all
|
|
# of the same under its own implementation id, with its own mutants caught;
|
|
# and the dense f64 state vector, run resident (the state on the device for
|
|
# the whole run), must come back with the CPU's bits on its pinned corpus,
|
|
# on the wider kernels' own corpus, at 16 and 20 qubits and on random
|
|
# circuits from |0...0> and from a given state, every state-vector mutant
|
|
# caught; and jobs placed on the fabric's members found there (the GPU by
|
|
# HIP and by OpenCL) must give the CPU's answers byte for byte, asked for by
|
|
# key and by `accelerator`, alone and with one state split over them, a
|
|
# state past a member refused, a job of wider gates placed, and auto
|
|
# placing a state on the GPU where the committed measurements say it is
|
|
# faster here, and on the CPU elsewhere.
|
|
# - hip-absent (a container with no GPU and no HIP libraries): the same build
|
|
# finds no member and says why, rather than failing; the device tests run over
|
|
# no member with the declaration `none`.
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- 'wai/wai-quantum/**'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-quantum-hip.yml'
|
|
schedule:
|
|
- cron: '43 6 * * *'
|
|
|
|
jobs:
|
|
# Does this pull request's own diff touch the paths below? This forge matches a
|
|
# workflow's `paths:` against the push, so a merge of main into a branch fires the
|
|
# workflow whatever the pull request changes; every job here waits on this answer.
|
|
# A push to main, a schedule, or anything the script cannot determine answers
|
|
# `true` (scripts/pr-touches-paths.py).
|
|
relevant:
|
|
name: does this change touch it
|
|
runs-on: linux-arm64
|
|
outputs:
|
|
run: ${{ steps.gate.outputs.run }}
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
- id: gate
|
|
run: |
|
|
set -euo pipefail
|
|
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-quantum-hip.yml \
|
|
"${{ github.event.pull_request.base.sha }}" \
|
|
"${{ github.event.pull_request.head.sha }}")
|
|
echo "this change touches wai-quantum-hip: $r"
|
|
echo "run=$r" >> "$GITHUB_OUTPUT"
|
|
|
|
hip-hw:
|
|
name: member · HIP (framework-128gb)
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux-amd
|
|
timeout-minutes: 90
|
|
env:
|
|
CARGO_PROFILE_DEV_DEBUG: '0'
|
|
CARGO_BUILD_JOBS: '12'
|
|
steps:
|
|
- name: Fetch the source
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
want="${{ github.event.pull_request.head.sha || github.sha }}"
|
|
rm -rf src && mkdir src && cd src
|
|
git init -q
|
|
# Fetch the commit itself, not the ref: a later commit on the ref that this
|
|
# workflow's paths do not match would otherwise be fetched instead. The
|
|
# host names its own route to the forge (WAI_FORGE_URL).
|
|
git fetch -q --depth 1 "${WAI_FORGE_URL:-http://100.64.224.0:3400}/Transaction-Science/open-standards.git" "$want"
|
|
git checkout -q FETCH_HEAD
|
|
got="$(git rev-parse HEAD)"
|
|
[ "$got" = "$want" ] || { echo "::error::fetched $got, not $want"; exit 1; }
|
|
- name: The member's unit tests
|
|
shell: bash
|
|
working-directory: src/wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-hip" nice -n 10 cargo test --locked --features quantum_fabric_hip,quantum_sv --lib -- quantum_fabric_hip:: quantum_fabric::hip:: quantum_fabric::sv64:: 2>&1 | tee "$RUNNER_TEMP/unit.log"
|
|
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/unit.log" || { echo "::error::no unit test ran"; exit 1; }
|
|
- name: The member's device tests
|
|
shell: bash
|
|
working-directory: src/wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
# The devices this host has, declared here (or by the runner, so a GPU
|
|
# and its declaration change in one place).
|
|
key="${WAI_QUANTUM_AMD_HIP_EXPECT:-gpu-hip-gfx1151}"
|
|
report="$RUNNER_TEMP/hip-report.txt"; : > "$report"
|
|
WAI_QUANTUM_HIP_EXPECT="$key" WAI_QUANTUM_HIP_REPORT="$report" CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-hip" nice -n 10 \
|
|
cargo test --locked --release --features quantum_fabric_hip --test hip_member -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/device.log"
|
|
grep -qE '^test result: ok\. 9 passed; 0 failed' "$RUNNER_TEMP/device.log" || { echo "::error::not every device test ran and passed"; exit 1; }
|
|
cat "$report"
|
|
grep -qx "hip devices: as declared ($key)" "$report" || { echo "::error::the devices found are not the declared ones"; exit 1; }
|
|
n="$(grep -c "^hip certified: key=$key .* parity=bit-exact " "$report")"
|
|
[ "$n" = 4 ] || { echo "::error::$n of 4 certificates were earned"; exit 1; }
|
|
grep -qx "hip mutants: key=$key killed 6 of 6" "$report" || { echo "::error::a kernel mutant survived"; exit 1; }
|
|
- name: The matrix-instruction kernel, every product by the instruction
|
|
shell: bash
|
|
working-directory: src/wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
key="${WAI_QUANTUM_AMD_HIP_EXPECT:-gpu-hip-gfx1151}"
|
|
report="$RUNNER_TEMP/hip-wmma-report.txt"; : > "$report"
|
|
WAI_QUANTUM_HIP_EXPECT="$key" WAI_QUANTUM_HIP_REPORT="$report" CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-hip" nice -n 10 \
|
|
cargo test --locked --release --features quantum_fabric_hip --test hip_wmma -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/wmma.log"
|
|
grep -qE '^test result: ok\. 9 passed; 0 failed' "$RUNNER_TEMP/wmma.log" || { echo "::error::not every matrix-instruction test ran and passed"; exit 1; }
|
|
cat "$report"
|
|
grep -qx "hip wmma devices: as declared ($key)" "$report" || { echo "::error::the devices found are not the declared ones"; exit 1; }
|
|
grep -q "^hip wmma member: key=$key .* implementation=hip-wmma-limbs8/[0-9a-f]\{16\} " "$report" || { echo "::error::the device ran no matrix-instruction member"; exit 1; }
|
|
n="$(grep -c "^hip wmma certified: key=$key .* implementation=hip-wmma-limbs8/[0-9a-f]\{16\} parity=bit-exact " "$report")"
|
|
[ "$n" = 4 ] || { echo "::error::$n of 4 matrix-instruction certificates were earned"; exit 1; }
|
|
grep -qx "hip wmma mutants: key=$key killed 8 of 8" "$report" || { echo "::error::a matrix-instruction mutant survived"; exit 1; }
|
|
- name: The dense f64 state vector, bit for bit
|
|
shell: bash
|
|
working-directory: src/wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
key="${WAI_QUANTUM_AMD_HIP_EXPECT:-gpu-hip-gfx1151}"
|
|
report="$RUNNER_TEMP/hip-sv-report.txt"; : > "$report"
|
|
WAI_QUANTUM_HIP_EXPECT="$key" WAI_QUANTUM_HIP_REPORT="$report" CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-hip" nice -n 10 \
|
|
cargo test --locked --release --features quantum_fabric_hip,quantum_sv --test hip_sv -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/sv.log"
|
|
grep -qE '^test result: ok\. 4 passed; 0 failed' "$RUNNER_TEMP/sv.log" || { echo "::error::not every state-vector test ran and passed"; exit 1; }
|
|
cat "$report"
|
|
grep -q "^hip sv certified: key=$key .* parity=bit-exact cases=48 digest=b7d129d0593adf70dc164477664970c5c0b8c09f79f1a0573036bd60888dbcd1$" "$report" || { echo "::error::the state vector is not the CPU's bit for bit"; exit 1; }
|
|
grep -q "^hip sv wide certified: key=$key .* kernel=wai.quantum.sv/run-wide .* parity=bit-exact cases=120 digest=093614f2386ec5120ab6ccde5187a544518432751591535a6f58c7a44061ebaf$" "$report" || { echo "::error::the wider kernels are not the CPU's bit for bit"; exit 1; }
|
|
grep -qx "hip sv mutants: key=$key killed 4 of 4" "$report" || { echo "::error::a state-vector mutant survived"; exit 1; }
|
|
grep -qx "hip sv random: key=$key 140 resident runs on every kernel match the CPU bit for bit" "$report" || { echo "::error::a random circuit's resident run is not the CPU's"; exit 1; }
|
|
- name: Jobs placed on the fabric's members, and a state split over them
|
|
shell: bash
|
|
working-directory: src/wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
# The members placement finds here, sorted (the GPU by HIP and by OpenCL),
|
|
# declared here or by the runner; one state is split over them.
|
|
keys="${WAI_QUANTUM_AMD_FABRIC_KEYS:-gpu-hip-gfx1151,opencl-gpu-1002}"
|
|
report="$RUNNER_TEMP/fabric-report.txt"; : > "$report"
|
|
WAI_QUANTUM_FABRIC_EXPECT="$keys" WAI_QUANTUM_FABRIC_SPLIT="$keys" WAI_QUANTUM_FABRIC_PAULI_EXPECT=none WAI_QUANTUM_FABRIC_REPORT="$report" CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-hip" nice -n 10 \
|
|
cargo test --locked --release --features quantum_engine,quantum_fabric,quantum_fabric_hip,quantum_fabric_opencl --test fabric_placement -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/fabric.log"
|
|
grep -qE '^test result: ok\. 7 passed; 0 failed' "$RUNNER_TEMP/fabric.log" || { echo "::error::not every placement test ran and passed"; exit 1; }
|
|
cat "$report"
|
|
grep -qx "fabric members: as declared ($keys)" "$report" || { echo "::error::the members placement found are not the declared ones"; exit 1; }
|
|
for key in ${keys//,/ }; do
|
|
grep -q "^fabric placed: key=$key .* give the CPU's answers byte for byte$" "$report" || { echo "::error::$key: a placed job's answers are not the CPU's"; exit 1; }
|
|
grep -q "^fabric refused: key=$key " "$report" || { echo "::error::$key: a state past what it holds was not refused"; exit 1; }
|
|
grep -qx "fabric wide: key=$key placed at fusion 5, the CPU's answers byte for byte" "$report" || { echo "::error::$key: a job of wider gates did not run with the CPU's answers"; exit 1; }
|
|
done
|
|
grep -qx "fabric split: ${keys//,/+} over 14 and 19 qubits gives the CPU's answers byte for byte" "$report" || { echo "::error::the state split over the members is not the CPU's"; exit 1; }
|
|
# The measurements show the integrated GPU faster here from 18 qubits at
|
|
# fusion 2 and 3 (engine::fabric_measured): auto places the measured job
|
|
# on it, and keeps a shallower one on the CPU.
|
|
# (A runner whose processor is not the one measured, by its model or its
|
|
# thread count, matches no row, and auto keeps every state on its CPU.)
|
|
if [ "$(grep -c '^fabric auto host: .* measured=true$' "$report")" = 1 ]; then
|
|
grep -qE "^fabric auto: by measurement, 18 qubits at fusion 2 on (gpu-hip-gfx1151|opencl-gpu-1002), the CPU's answers byte for byte; a shallower job on the CPU$" "$report" || { echo "::error::auto did not follow the measurements"; exit 1; }
|
|
else
|
|
grep -qx "fabric auto: no member here measured faster; every state on the CPU, its result unnamed" "$report" || { echo "::error::auto placed a state on a host not measured"; exit 1; }
|
|
fi
|
|
grep -q "^fabric accelerator: .* the CPU's answers byte for byte" "$report" || { echo "::error::an accelerator request placed no state"; exit 1; }
|
|
# The pauli method's members are CUDA's alone: none here, and it says so.
|
|
grep -qx "fabric pauli: no member here certified for it; accelerator refused" "$report" || { echo "::error::a pauli job was placed with no member for it"; exit 1; }
|
|
|
|
hip-absent:
|
|
name: member · HIP absent (linux-arm64)
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux-arm64
|
|
timeout-minutes: 60
|
|
env:
|
|
WAI_QUANTUM_HIP_EXPECT: none
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- name: Install Rust 1.98.0
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
command -v rustup >/dev/null 2>&1 || \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
- name: No HIP here, and the member says so
|
|
shell: bash
|
|
working-directory: wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
report="$RUNNER_TEMP/hip-report.txt"; : > "$report"
|
|
WAI_QUANTUM_HIP_REPORT="$report" cargo test --locked --features quantum_fabric_hip --test hip_member -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/device.log"
|
|
grep -qE '^test result: ok\. 9 passed; 0 failed' "$RUNNER_TEMP/device.log" || { echo "::error::not every device test ran and passed"; exit 1; }
|
|
grep -qx 'hip devices: as declared (none)' "$report" || { echo "::error::the absent case was not established"; exit 1; }
|
|
WAI_QUANTUM_HIP_REPORT="$report" cargo test --locked --features quantum_fabric_hip --test hip_wmma -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/wmma.log"
|
|
grep -qE '^test result: ok\. 9 passed; 0 failed' "$RUNNER_TEMP/wmma.log" || { echo "::error::the matrix-instruction tests did not run over no device"; exit 1; }
|
|
grep -qx 'hip wmma devices: as declared (none)' "$report" || { echo "::error::the matrix-instruction absent case was not established"; exit 1; }
|
|
cargo test --locked --features quantum_fabric_hip,quantum_sv --test hip_sv -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/sv.log"
|
|
grep -qE '^test result: ok\. 4 passed; 0 failed' "$RUNNER_TEMP/sv.log" || { echo "::error::the state-vector tests did not run over no device"; exit 1; }
|
|
WAI_QUANTUM_FABRIC_EXPECT=none WAI_QUANTUM_FABRIC_REPORT="$report" cargo test --locked --features quantum_engine,quantum_fabric,quantum_fabric_hip --test fabric_placement -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/fabric.log"
|
|
grep -qE '^test result: ok\. 7 passed; 0 failed' "$RUNNER_TEMP/fabric.log" || { echo "::error::the placement tests did not run over no member"; exit 1; }
|
|
grep -qx 'fabric members: as declared (none)' "$report" || { echo "::error::the placement's absent case was not established"; exit 1; }
|