A jeff register's size reached Vec::with_capacity before the qubit cap, so one of 2^32 - 1 qubits asked for 34 GB: under wasm32-wasip2 the test written first trapped in qureg_op. It is now refused (Q111) before anything is made. The audit of every allocation and loop sized by the input: - the wire reader bounds every pointer, segment and list in 64 bits (a segment count of u32::MAX overflowed a 32-bit usize), and counts each list element against the words it may visit; - the jeff reader counts every register slot, array element (an array copied by setIndex included), call frame and condition term against one budget, caps arrays, values per function, operations run and gates written, shares conditions between copies, and no longer truncates a 64-bit size, index, selector or shift to fit; - the QIR reader no longer copies a block's lines on each visit, bounds the nesting of branches, the instructions run over every path and the state each path copies, and dedups a condition's reads; - a condition is at most 1 024 terms and 32 deep once written, a run of one operator written flat; - a conversion's check is not run past 262 144 gates applied or 4e9 amplitudes, and says so; both readers take modules of up to 16 MiB. tests/formats_mutation.rs mutates every QIR and jeff file of the conformance set from a fixed seed (45 153 inputs: bits flipped, files cut short and spliced, every length, count, offset and 32-bit field inflated) and holds each to converting or a refusal with a code of the table, within 20 s and 1 GiB measured by a counting allocator. wai-quantum-formats.yml runs it natively and under wasm32-wasip2 (a release build with overflow checks), the count pinned. Co-Authored-By: Claude Opus 5.5 <[email protected]>
174 lines
8.7 KiB
YAML
174 lines
8.7 KiB
YAML
name: wai-quantum other formats
|
|
|
|
# The readers and writers of other formats (wai/wai-quantum/src/engine/formats/):
|
|
# QIR under its base and adaptive profiles and jeff through the crate's own
|
|
# Cap'n Proto reader and writer. Their unit tests (the wire format, the
|
|
# OpenQASM they write, every intrinsic and gate held to its matrix, each
|
|
# profile's refusals with their lines, the Pauli-product rotation against
|
|
# exp(iθP), every prefix of a module refused), and the conformance set
|
|
# (wai/quantum-conformance/formats/: the QIR specification's examples, the
|
|
# jeff project's benchmarks and examples, hand-built modules and OpenQASM
|
|
# sources written to each format, each with what the engine must do with it,
|
|
# the QIR written pinned by its BLAKE3), and hostile inputs: every file
|
|
# of the set mutated, each converted or refused with a code within its
|
|
# bounds, natively and under wasm32-wasip2.
|
|
#
|
|
# The set runs natively, where a second test holds its bundle equal to its
|
|
# files, and under wasm32-wasip2 by wasmtime, where the cases come from the
|
|
# bundle the test compiles in and the QIR written must be the same bytes.
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/quantum-conformance/formats/**'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-quantum-formats.yml'
|
|
pull_request:
|
|
paths:
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/quantum-conformance/formats/**'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-quantum-formats.yml'
|
|
|
|
jobs:
|
|
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-formats.yml \
|
|
"${{ github.event.pull_request.base.sha }}" \
|
|
"${{ github.event.pull_request.head.sha }}")
|
|
echo "this change touches the readers of other formats: $r"
|
|
echo "run=$r" >> "$GITHUB_OUTPUT"
|
|
|
|
native:
|
|
name: readers, writers and the set · linux
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux
|
|
timeout-minutes: 60
|
|
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"
|
|
# A module filter is satisfied by any one test of the module, so each
|
|
# module's tests are required by name to have run.
|
|
- name: The readers' and writers' unit tests
|
|
shell: bash
|
|
working-directory: wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --release --features full --lib engine::formats 2>&1 | tee "$RUNNER_TEMP/formats.log"
|
|
grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$RUNNER_TEMP/formats.log" || { echo "::error::the formats' tests did not pass"; exit 1; }
|
|
for m in engine::formats::capnp::tests engine::formats::emit::tests engine::formats::tests::every_gate_is_written_exactly engine::formats::tests::a_pauli_product_rotation_is_exp_i_theta_p engine::formats::tests::the_conformance_set_holds_these_modules engine::formats::tests::a_register_too_large_is_refused_before_it_is_allocated; do
|
|
grep -q "^test $m.* \.\.\. ok$" "$RUNNER_TEMP/formats.log" || { echo "::error::no test of $m ran"; exit 1; }
|
|
done
|
|
- name: The conformance set
|
|
shell: bash
|
|
working-directory: wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --release --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/set.log"
|
|
grep -q 'test result: ok. 2 passed; 0 failed' "$RUNNER_TEMP/set.log" || { echo "::error::the conformance set did not pass"; exit 1; }
|
|
# Hostile inputs: every file of the set mutated by a seeded generator
|
|
# (bits flipped, files cut short and spliced, and every length, count,
|
|
# offset and 32-bit field inflated), each converted or refused with a
|
|
# code within its time and memory bounds. The count of inputs is pinned.
|
|
- name: Hostile inputs
|
|
shell: bash
|
|
working-directory: wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --release --features full --test formats_mutation -- --nocapture 2>&1 | tee "$RUNNER_TEMP/mutation.log"
|
|
grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/mutation.log" || { echo "::error::a hostile input was not refused cleanly"; exit 1; }
|
|
grep -q '45153 inputs: ' "$RUNNER_TEMP/mutation.log" || { echo "::error::the count of hostile inputs is not the pinned 45153"; exit 1; }
|
|
- name: The readers without the receipts
|
|
shell: bash
|
|
working-directory: wai/wai-quantum
|
|
run: |
|
|
set -euo pipefail
|
|
cargo check --locked --no-default-features --features quantum_engine --lib
|
|
|
|
wasip2:
|
|
name: the set under wasm32-wasip2 (wasmtime)
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- name: Install Rust 1.98.0 with the wasm32-wasip2 target
|
|
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
|
|
rustup target add --toolchain 1.98.0 wasm32-wasip2
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
# The release binary, pinned by version and SHA-256, rather than a build from source.
|
|
- name: Install wasmtime 49.0.2
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
[ "$(uname -m)" = x86_64 ] || { echo "::error::the pinned wasmtime archive is x86_64; this host is $(uname -m)"; exit 1; }
|
|
if ! command -v xz >/dev/null 2>&1; then
|
|
SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo"
|
|
$SUDO apt-get update -qq
|
|
$SUDO apt-get install -y -qq xz-utils
|
|
fi
|
|
v=49.0.2
|
|
f="wasmtime-v$v-x86_64-linux.tar.xz"
|
|
curl --proto '=https' --tlsv1.2 -sSfL -o "$RUNNER_TEMP/$f" \
|
|
"https://github.com/bytecodealliance/wasmtime/releases/download/v$v/$f"
|
|
echo "a4d6e9e3a5a60f527cf7793d674c48930c80c2e8977995b8a275cad3254b9322 $RUNNER_TEMP/$f" | sha256sum -c -
|
|
tar -xJf "$RUNNER_TEMP/$f" -C "$RUNNER_TEMP"
|
|
echo "$RUNNER_TEMP/wasmtime-v$v-x86_64-linux" >> "$GITHUB_PATH"
|
|
# A debug build, so integer overflow panics instead of wrapping. The test
|
|
# that reads the set's files has no wasip2 build; the one that runs the
|
|
# cases from the compiled-in bundle does.
|
|
- name: The conformance set under wasmtime
|
|
shell: bash
|
|
working-directory: wai/wai-quantum
|
|
env:
|
|
CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --target wasm32-wasip2 --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/wasip2.log"
|
|
grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2.log" || { echo "::error::the conformance set did not pass under wasip2"; exit 1; }
|
|
# The same hostile inputs where an allocation larger than the address
|
|
# space panics: a release build with overflow checks on, as it is slow
|
|
# enough otherwise to outlast the job.
|
|
- name: Hostile inputs under wasmtime
|
|
shell: bash
|
|
working-directory: wai/wai-quantum
|
|
env:
|
|
CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime
|
|
CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS: "true"
|
|
run: |
|
|
set -euo pipefail
|
|
cargo test --locked --release --target wasm32-wasip2 --features full --test formats_mutation -- --nocapture 2>&1 | tee "$RUNNER_TEMP/wasip2-mutation.log"
|
|
grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2-mutation.log" || { echo "::error::a hostile input was not refused cleanly under wasip2"; exit 1; }
|
|
grep -q '45153 inputs: ' "$RUNNER_TEMP/wasip2-mutation.log" || { echo "::error::the count of hostile inputs under wasip2 is not the pinned 45153"; exit 1; }
|