wai-quantum: one OpenQASM 3 reader for every surface, every refusal and warning under a stable code #519

Closed
dcharlot wants to merge 28 commits from wai/quantum-qasm3 into main
256 changed files with 83536 additions and 2580 deletions

View file

@ -16,12 +16,14 @@ on:
paths:
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-cli/**'
- 'wai/wai-quantum-qpu/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-cli.yml'
pull_request:
paths:
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-cli/**'
- 'wai/wai-quantum-qpu/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-cli.yml'
@ -69,6 +71,15 @@ jobs:
set -euo pipefail
cargo test --locked --release 2>&1 | tee "$RUNNER_TEMP/test.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; }
# Your own providers end to end (tests/providers.rs), against the
# reference provider this binary serves, each test by name.
for t in a_person_adds_tests_plans_and_runs_on_a_provider_and_the_receipt_verifies \
a_run_that_is_not_free_is_refused_until_the_person_confirms_it \
a_credential_is_kept_in_a_file_only_its_owner_reads_and_appears_in_nothing_printed \
a_provider_over_http_runs_with_its_credential_in_the_header \
an_agent_lists_estimates_is_refused_and_runs_only_with_confirm_cost; do
grep -q "^test $t \.\.\. ok$" "$RUNNER_TEMP/test.log" || { echo "::error::$t did not run and pass"; exit 1; }
done
cargo clippy --locked --release --all-targets -- -D warnings
cargo clippy --locked --release --all-targets --no-default-features -- -D warnings
- name: The smoke checks

View file

@ -0,0 +1,180 @@
name: wai-quantum editors
# The engine in editors:
# - the language service (wai/wai-quantum/src/engine/editor): its unit tests,
# each module by name, on Linux and macOS;
# - the language server, `wai-quantum lsp`, driven over standard input and
# output by a scripted session from initialize to exit (tests/lsp.rs), on
# Linux and macOS;
# - `wai-quantum fmt` on every program in the repository: formatting the
# formatted program changes nothing, and the engine draws the same circuit;
# - the tree-sitter grammar (wai/tools/tree-sitter-openqasm): regenerated
# with the pinned command line and identical to the committed parser, its
# corpus and highlighting tests, and every program in the repository's
# conformance material and examples parsed with no error;
# - the editor extension (wai/tools/wai-quantum-editor): built, packaged as a
# .vsix with the pinned packaging tool, and checked (its manifest against
# the server, its highlighting, its message when the command line is
# missing, the other editors' configurations, and the package's files);
# - the extension for editors with WebAssembly extensions, built for
# wasm32-wasip2.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-cli/**'
- 'wai/tools/tree-sitter-openqasm/**'
- 'wai/tools/wai-quantum-editor/**'
- 'wai/quantum-studio/src/examples.js'
- 'wai/quantum-ops-conformance/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-editors.yml'
pull_request:
paths:
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-cli/**'
- 'wai/tools/tree-sitter-openqasm/**'
- 'wai/tools/wai-quantum-editor/**'
- 'wai/quantum-studio/src/examples.js'
- 'wai/quantum-ops-conformance/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-editors.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-editors.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches the editors' support: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
editors-linux:
name: editors · 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 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"
- uses: actions/setup-node@v4
with:
node-version: '22'
# The filters are module paths, and a filter that matches nothing passes, so
# each module must show a test that passed.
- name: The language service's tests
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
cargo test --locked --features full --lib engine::editor:: 2>&1 | tee "$RUNNER_TEMP/service.log"
for m in lex index gates format tests; do
grep -qE "^test engine::editor::$m::.* \.\.\. ok$" "$RUNNER_TEMP/service.log" || { echo "::error::no test of engine::editor::$m ran"; exit 1; }
done
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/service.log"
- name: The language server over standard input and output, and the verbs' parsing
shell: bash
working-directory: wai/wai-quantum-cli
run: |
set -euo pipefail
cargo test --locked --release --test lsp 2>&1 | tee "$RUNNER_TEMP/lsp.log"
grep -qE '^test result: ok\. 2 passed; 0 failed' "$RUNNER_TEMP/lsp.log" || { echo "::error::the protocol session did not pass"; exit 1; }
cargo test --locked --release --bin wai-quantum -- lsp:: the_command_line_parses_as_documented 2>&1 | tee "$RUNNER_TEMP/unit.log"
grep -qE '^test result: ok\. 4 passed; 0 failed' "$RUNNER_TEMP/unit.log" || { echo "::error::the server's unit tests did not all run"; exit 1; }
cargo build --locked --release
- name: The grammar, regenerated as committed; its corpus, its highlighting, and every program in the repository
shell: bash
working-directory: wai/tools/tree-sitter-openqasm
run: |
set -euo pipefail
npm ci --no-audit --no-fund
npx tree-sitter generate --abi 14
git diff --exit-code -- src || { echo "::error::src/ is not what grammar.js generates: regenerate and commit it"; exit 1; }
npx tree-sitter test
node script/check-programs.mjs --out "$RUNNER_TEMP/programs"
- name: wai-quantum fmt on every program in the repository
shell: bash
run: |
set -euo pipefail
w="$PWD/wai/wai-quantum-cli/target/release/wai-quantum"
n=0
for f in "$RUNNER_TEMP"/programs/*.qasm; do
"$w" fmt "$f" > "$RUNNER_TEMP/once.qasm"
"$w" fmt "$RUNNER_TEMP/once.qasm" > "$RUNNER_TEMP/twice.qasm"
cmp -s "$RUNNER_TEMP/once.qasm" "$RUNNER_TEMP/twice.qasm" || { echo "::error::formatting $f again changed it"; diff "$RUNNER_TEMP/once.qasm" "$RUNNER_TEMP/twice.qasm" || true; exit 1; }
# The engine draws the formatted program as it draws the program, or refuses
# both (a refusal names a line, which formatting may move).
args=(); grep -qE '^\s*input\s+u?int' "$f" && args=(--input n=5)
a_out=$("$w" "${args[@]}" draw < "$f" 2>/dev/null) && a=0 || a=$?
b_out=$("$w" "${args[@]}" draw < "$RUNNER_TEMP/once.qasm" 2>/dev/null) && b=0 || b=$?
[ "$a" = "$b" ] || { echo "::error::the engine reads $f (status $a) and its formatted form (status $b) differently"; exit 1; }
[ "$a" != 0 ] || [ "$a_out" = "$b_out" ] || { echo "::error::the engine draws the formatted $f differently"; diff <(echo "$a_out") <(echo "$b_out") || true; exit 1; }
n=$((n + 1))
done
[ "$n" -ge 20 ] || { echo "::error::only $n programs were formatted"; exit 1; }
echo "fmt: $n programs, each stable, each drawn the same or refused alike"
- name: The editor extension, packaged and checked
shell: bash
working-directory: wai/tools/wai-quantum-editor
run: |
set -euo pipefail
npm ci --no-audit --no-fund
npm run package < /dev/null
WAI_QUANTUM_BIN="$GITHUB_WORKSPACE/wai/wai-quantum-cli/target/release/wai-quantum" node test/check.mjs --vsix wai-quantum.vsix
- name: The extension for editors with WebAssembly extensions, for wasm32-wasip2
shell: bash
working-directory: wai/tools/wai-quantum-editor/editors/extension
run: cargo build --locked --release --target wasm32-wasip2
editors-macos:
name: editors · macos
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: macos
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
# launchd's PATH omits cargo on the macOS host runner.
- name: Put cargo on PATH
shell: bash
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: The language service's tests
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
nice -n 10 cargo test --locked --features full --lib engine::editor:: 2>&1 | tee "$RUNNER_TEMP/service.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/service.log"
- name: The language server over standard input and output
shell: bash
working-directory: wai/wai-quantum-cli
run: |
set -euo pipefail
nice -n 10 cargo test --locked --release --test lsp 2>&1 | tee "$RUNNER_TEMP/lsp.log"
grep -qE '^test result: ok\. 2 passed; 0 failed' "$RUNNER_TEMP/lsp.log" || { echo "::error::the protocol session did not pass"; exit 1; }

147
.github/workflows/wai-quantum-lang.yml vendored Normal file
View file

@ -0,0 +1,147 @@
name: wai-quantum OpenQASM reader
# The OpenQASM reader every surface uses (wai/wai-quantum/src/qasm.rs and
# src/qasm/): its unit tests (the lexer, the parser, the elaborator, the
# diagnostic table and the page that documents it, every modifier against the
# matrix it must equal, every condition compiled to steps against every value
# of its bits), and the OpenQASM 3 conformance corpus
# (wai/quantum-conformance/qasm3/: the specification's example programs and a
# program for each construct, each with what the reader must do with it).
#
# The corpus 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. The browser build runs the corpus too
# (wai-web-wasm.yml, wai-web's wasm32 tests). The reader is also built without
# the lowerings (features quantum_lang and quantum_toolchain), as the edge
# component builds it.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'wai/wai-quantum/**'
- 'wai/quantum-conformance/qasm3/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-lang.yml'
pull_request:
paths:
- 'wai/wai-quantum/**'
- 'wai/quantum-conformance/qasm3/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-lang.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-lang.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches the OpenQASM reader: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
native:
name: reader and corpus · 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 reader's unit tests
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
cargo test --locked --release --features full --lib qasm 2>&1 | tee "$RUNNER_TEMP/qasm.log"
grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$RUNNER_TEMP/qasm.log" || { echo "::error::the reader's tests did not pass"; exit 1; }
for m in qasm::diag qasm::lex qasm::parse qasm::value qasm::lang_tests qasm::tests quantum_toolchain::tests::qasm_round_trip; do
grep -q "^test $m.* \.\.\. ok$" "$RUNNER_TEMP/qasm.log" || { echo "::error::no test of $m ran"; exit 1; }
done
- name: The conformance corpus
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
cargo test --locked --release --features full --test qasm3_conformance 2>&1 | tee "$RUNNER_TEMP/corpus.log"
grep -q 'test result: ok. 2 passed; 0 failed' "$RUNNER_TEMP/corpus.log" || { echo "::error::the corpus did not pass"; exit 1; }
- name: The reader without the lowerings
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
cargo check --locked --no-default-features --features quantum_lang --lib
cargo check --locked --no-default-features --features quantum_toolchain --lib
wasip2:
name: corpus 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 corpus's files has no wasip2 build; the one that runs the
# cases from the compiled-in bundle does.
- name: The conformance corpus 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 qasm3_conformance 2>&1 | tee "$RUNNER_TEMP/wasip2.log"
grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2.log" || { echo "::error::the corpus did not pass under wasip2"; exit 1; }

View file

@ -0,0 +1,213 @@
name: wai-quantum for Python, release wheels
# On a tag wai-quantum-cli-v<version> (the command line's release), builds the Python
# package's wheels (wai/wai-quantum-py) and attaches them to the same forge release,
# each with its SHA-256. One abi3 wheel per platform serves every Python from 3.9 on:
# macOS arm64 and x86_64 (the x86_64 one cross-built on the Apple-silicon runner);
# Linux x86_64 and arm64, manylinux2014 (glibc 2.17), linked with zig so the
# runner's newer glibc does not leak into the wheel;
# Windows x86_64.
# Each wheel that runs on its runner is installed alone on Python 3.9 in a fresh venv
# and runs tests/smoke.py before it is attached. Nothing is published to a package
# index.
#
# Uploads go to the forge directly over the private network (FORGE_API), never through
# the public tunnel. A runner that is down leaves its platform's wheel missing: the
# release lists what each build attached.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
tags: ['wai-quantum-cli-v*']
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
FORGE_API: http://100.64.224.0:3400/api/v1
FORGE_REPO: Transaction-Science/open-standards
MATURIN_VERSION: 1.15.0
ZIG_VERSION: 0.16.0
jobs:
version:
name: the tag is the command line's version
runs-on: linux
outputs:
version: ${{ steps.v.outputs.version }}
steps:
- uses: actions/checkout@v5
- id: v
shell: bash
run: |
set -euo pipefail
v="${GITHUB_REF_NAME#wai-quantum-cli-v}"
crate="$(sed -n 's/^version = "\(.*\)"$/\1/p' wai/wai-quantum-cli/Cargo.toml | head -1)"
[ "$v" = "$crate" ] || { echo "::error::the tag says $v, wai-quantum-cli/Cargo.toml says $crate"; exit 1; }
echo "the Python package: $(sed -n 's/^version = "\(.*\)"$/\1/p' wai/wai-quantum-py/Cargo.toml | head -1)"
echo "version=$v" >> "$GITHUB_OUTPUT"
macos:
name: macOS · ${{ matrix.triple }}
needs: version
runs-on: macos
timeout-minutes: 60
strategy:
fail-fast: false
max-parallel: 1
matrix:
triple: [aarch64-apple-darwin, x86_64-apple-darwin]
steps:
- uses: actions/checkout@v5
# launchd's PATH omits cargo and uv on the macOS host runner.
- name: Put cargo and uv on PATH
shell: bash
run: |
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Build, check and hash
shell: bash
run: |
set -euo pipefail
command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
t="${{ matrix.triple }}"
rustup target add "$t"
tools="$RUNNER_TEMP/tools"; dist="$RUNNER_TEMP/dist"; rm -rf "$tools" "$dist"
uv venv -q --python 3.12 "$tools"
uv pip install -q --python "$tools/bin/python" "maturin==$MATURIN_VERSION"
(cd wai/wai-quantum-py && nice -n 10 "$tools/bin/maturin" build --release --locked --target "$t" -o "$dist")
whl="$(ls "$dist"/wai_quantum-*.whl)"
if [ "$t" = "$(rustc -vV | sed -n 's/^host: //p')" ]; then
py="$RUNNER_TEMP/py39"; rm -rf "$py"
uv venv -q --python 3.9 "$py"
uv pip install -q --python "$py/bin/python" --no-deps "$whl"
(cd "$RUNNER_TEMP" && "$py/bin/python" -I "$GITHUB_WORKSPACE/wai/wai-quantum-py/tests/smoke.py")
fi
(cd "$dist" && for f in *.whl; do shasum -a 256 "$f" > "$f.sha256"; done; ls -l)
- name: Attach to the release
shell: bash
env:
FORGE_TOKEN: ${{ github.token }}
run: bash wai/wai-quantum-cli/release/publish.sh "$GITHUB_REF_NAME" "wai-quantum ${{ needs.version.outputs.version }}" "$RUNNER_TEMP"/dist/*
linux:
name: Linux · ${{ matrix.triple }}
needs: version
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- runner: linux
triple: x86_64-unknown-linux-gnu
- runner: linux-arm64
triple: aarch64-unknown-linux-gnu
steps:
- uses: actions/checkout@v5
- name: Install Rust 1.98.0 and uv
shell: bash
run: |
export PATH="$HOME/.local/bin:$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 "${{ matrix.triple }}"
command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Build (manylinux2014, with zig), check and hash
shell: bash
run: |
set -euo pipefail
t="${{ matrix.triple }}"
tools="$RUNNER_TEMP/tools"; dist="$RUNNER_TEMP/dist"; rm -rf "$tools" "$dist"
uv venv -q --python 3.12 "$tools"
uv pip install -q --python "$tools/bin/python" "maturin==$MATURIN_VERSION" "ziglang==$ZIG_VERSION"
export PATH="$tools/bin:$PATH"
(cd wai/wai-quantum-py && maturin build --release --locked --zig --compatibility manylinux2014 --target "$t" -o "$dist")
whl="$(ls "$dist"/wai_quantum-*.whl)"
case "$whl" in *manylinux2014*) ;; *) echo "::error::not a manylinux2014 wheel: $whl"; exit 1 ;; esac
py="$RUNNER_TEMP/py39"; rm -rf "$py"
uv venv -q --python 3.9 "$py"
uv pip install -q --python "$py/bin/python" --no-deps "$whl"
(cd "$RUNNER_TEMP" && "$py/bin/python" -I "$GITHUB_WORKSPACE/wai/wai-quantum-py/tests/smoke.py")
(cd "$dist" && for f in *.whl; do sha256sum "$f" > "$f.sha256"; done; ls -l)
- name: Attach to the release
shell: bash
env:
FORGE_TOKEN: ${{ github.token }}
run: bash wai/wai-quantum-cli/release/publish.sh "$GITHUB_REF_NAME" "wai-quantum ${{ needs.version.outputs.version }}" "$RUNNER_TEMP"/dist/*
windows:
name: Windows · x86_64-pc-windows-msvc
needs: version
runs-on: windows
timeout-minutes: 60
env:
CI: ""
steps:
- uses: actions/checkout@v5
- name: Build, check and hash
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
Remove-Item Env:CI -ErrorAction SilentlyContinue
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
powershell -ExecutionPolicy Bypass -c "irm https://astral.sh/uv/install.ps1 | iex"
$env:Path = "$env:USERPROFILE\.local\bin;$env:Path"
}
$tools = Join-Path $env:RUNNER_TEMP 'tools'
$dist = Join-Path $env:RUNNER_TEMP 'dist'
$py39 = Join-Path $env:RUNNER_TEMP 'py39'
Remove-Item $tools, $dist, $py39 -Recurse -Force -ErrorAction Ignore
uv venv -q --python 3.12 $tools
if ($LASTEXITCODE -ne 0) { throw "no venv for the build tools" }
uv pip install -q --python "$tools\Scripts\python.exe" "maturin==$env:MATURIN_VERSION"
if ($LASTEXITCODE -ne 0) { throw "maturin did not install" }
Push-Location wai\wai-quantum-py
& "$tools\Scripts\maturin.exe" build --release --locked --target x86_64-pc-windows-msvc -o $dist
if ($LASTEXITCODE -ne 0) { throw "the wheel did not build" }
Pop-Location
$whl = (Get-ChildItem $dist -Filter 'wai_quantum-*.whl').FullName
uv venv -q --python 3.9 $py39
uv pip install -q --python "$py39\Scripts\python.exe" --no-deps $whl
if ($LASTEXITCODE -ne 0) { throw "the wheel did not install" }
Push-Location $env:RUNNER_TEMP
& "$py39\Scripts\python.exe" -I (Join-Path $env:GITHUB_WORKSPACE 'wai\wai-quantum-py\tests\smoke.py')
if ($LASTEXITCODE -ne 0) { throw "the smoke checks failed" }
Pop-Location
foreach ($f in Get-ChildItem $dist -Filter '*.whl') {
$h = (Get-FileHash $f.FullName -Algorithm SHA256).Hash.ToLower()
Set-Content -NoNewline -Path "$($f.FullName).sha256" -Value "$h $($f.Name)`n"
}
- name: Attach to the release
shell: pwsh
env:
FORGE_TOKEN: ${{ github.token }}
run: |
$ErrorActionPreference = 'Stop'
$H = @{ Authorization = "token $env:FORGE_TOKEN" }
$base = "$env:FORGE_API/repos/$env:FORGE_REPO/releases"
$tag = $env:GITHUB_REF_NAME
$rel = $null
for ($i = 0; $i -lt 6 -and -not $rel; $i++) {
try { $rel = Invoke-RestMethod -Headers $H -Uri "$base/tags/$tag" } catch {
try { Invoke-RestMethod -Headers $H -Method Post -Uri $base -ContentType 'application/json' -Body (@{ tag_name = $tag; name = "wai-quantum ${{ needs.version.outputs.version }}" } | ConvertTo-Json) | Out-Null } catch {}
Start-Sleep -Seconds 3
}
}
if (-not $rel) { throw "no release for $tag" }
foreach ($f in Get-ChildItem (Join-Path $env:RUNNER_TEMP 'dist')) {
foreach ($a in (Invoke-RestMethod -Headers $H -Uri "$base/$($rel.id)/assets" | Where-Object { $_.name -eq $f.Name })) {
Invoke-RestMethod -Headers $H -Method Delete -Uri "$base/$($rel.id)/assets/$($a.id)" | Out-Null
}
curl.exe -fsS -H "Authorization: token $env:FORGE_TOKEN" -X POST "$base/$($rel.id)/assets?name=$($f.Name)" -F "attachment=@$($f.FullName);type=application/octet-stream" -o NUL -w "uploaded $($f.Name): http %{http_code}`n"
if ($LASTEXITCODE -ne 0) { throw "the upload of $($f.Name) failed" }
}

100
.github/workflows/wai-quantum-py.yml vendored Normal file
View file

@ -0,0 +1,100 @@
name: wai-quantum for Python
# The Python package (wai/wai-quantum-py): the shared job engine as a pyo3 extension
# module (abi3, Python 3.9 on), the service client and the notebook widgets. On
# Linux and on macOS, ci/check.sh builds the command line and the job service from
# this checkout, clippy on the module, the wheel with maturin, installs the wheel
# in a fresh venv with the pinned and hashed test requirements, runs pytest with
# every test required (a job run in Python equals the command line's and the
# service's; receipts verify across them; the widgets' front ends run under node),
# executes the doc notebooks against a job service started here and holds each to
# its stored outputs, and installs the same wheel alone on Python 3.9.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-py/**'
- 'wai/wai-quantum-cli/**'
- 'wai/wai-quantum-service/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-py.yml'
pull_request:
paths:
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-py/**'
- 'wai/wai-quantum-cli/**'
- 'wai/wai-quantum-service/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-py.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-py.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches the Python package: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
py-linux:
name: python · linux
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 90
steps:
- uses: actions/checkout@v5
- name: Install Rust 1.98.0 with clippy, and uv
shell: bash
run: |
export PATH="$HOME/.local/bin:$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 --component clippy
rustup default 1.98.0
command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
command -v node >/dev/null 2>&1 || { echo "::error::no node on this runner: the widgets' front ends are tested with it"; exit 1; }
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Build, test and run the notebooks
shell: bash
run: bash wai/wai-quantum-py/ci/check.sh
py-macos:
name: python · macos
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: macos
timeout-minutes: 90
steps:
- uses: actions/checkout@v5
# launchd's PATH omits cargo, uv and node on the macOS host runner.
- name: Put cargo, uv and node on PATH
shell: bash
run: |
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "/opt/homebrew/bin" >> "$GITHUB_PATH"
- name: Build, test and run the notebooks
shell: bash
run: |
set -euo pipefail
command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
nice -n 10 bash wai/wai-quantum-py/ci/check.sh

View file

@ -5,7 +5,14 @@ name: wai-quantum-qpu
# in the provider's format, runs it on wai-quantum's simulator, and answers in
# the provider's result shape and bit order), certification through the
# fabric, the probe that catches a service with another bit order, and curl
# end to end against a local server. No request leaves the runner.
# end to end against a local server. And the person's own providers: the
# open provider protocol against the reference provider over both
# transports (a local process and HTTP through curl), its conformance
# vectors, the reference's pinned counts, the registry with its credential
# in a file only its owner reads, the refusal of a run that is not free
# until it is confirmed, receipts that verify, and no credential in
# anything a run returns; and, on macOS, the keychain in a keychain file
# made for the test. No request leaves the runner, and nothing spends money.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -68,10 +75,45 @@ jobs:
run: |
set -euo pipefail
command -v curl >/dev/null || { echo "::error::no curl: the transport test would pass over nothing"; exit 1; }
# The credentials go to files here: CI has no keychain, and the
# tests never touch one but the macOS leg's own.
export WAI_QUANTUM_CREDENTIALS=file
cargo test --locked 2>&1 | tee "$RUNNER_TEMP/test.log"
grep -qE '^test result: ok\. 8 passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::not every client test ran and passed"; exit 1; }
grep -q '^test curl_carries_a_request_to_a_server_and_back \.\.\. ok$' "$RUNNER_TEMP/test.log" || { echo "::error::the transport test did not run"; exit 1; }
# The library (33), the clients against their contracts (8), and the
# providers end to end (12), each binary's count pinned.
for n in 33 8 12; do
grep -qE "^test result: ok\. $n passed; 0 failed" "$RUNNER_TEMP/test.log" || { echo "::error::a test binary did not pass all $n of its tests"; exit 1; }
done
for t in curl_carries_a_request_to_a_server_and_back every_operation_over_http every_operation_over_a_local_process \
the_reference_conforms_over_both_transports the_reference_gives_its_pinned_counts_on_every_platform \
a_run_that_is_not_free_is_refused_until_confirmed_and_carries_its_cost \
a_credential_reaches_the_provider_and_nothing_a_run_returns; do
grep -q "^test $t \.\.\. ok$" "$RUNNER_TEMP/test.log" || { echo "::error::$t did not run and pass"; exit 1; }
done
- name: Clippy
shell: bash
working-directory: wai/wai-quantum-qpu
run: cargo clippy --locked --all-targets --no-deps -- -D warnings
keychain:
name: credentials in the keychain (macos)
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: macos
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- name: Put cargo on PATH
shell: bash
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
# The registry's tests, the keychain's among them: a credential kept,
# read back, replaced and forgotten through the system tool, in a
# keychain file the test makes and deletes (the runner's own keychain
# is never touched).
- name: The keychain test
shell: bash
working-directory: wai/wai-quantum-qpu
run: |
set -euo pipefail
nice -n 10 cargo test --locked --lib registry:: 2>&1 | tee "$RUNNER_TEMP/keychain.log"
grep -q '^test registry::tests::the_macos_keychain_keeps_reads_and_forgets_a_credential \.\.\. ok$' "$RUNNER_TEMP/keychain.log" || { echo "::error::the keychain test did not run and pass"; exit 1; }

View file

@ -7,6 +7,8 @@ name: wai-quantum studio and desktop
# planned as it is typed, run here with its receipt checked here, an error marked
# on its line, any angle on the state vector, Ctrl+Enter, and the program and the
# history kept across a reload; nothing scrolls sideways at a phone's width;
# - test/hardware.test.mjs, without a browser: "Your hardware" asks the person before
# a run that is not free, never before a free one, and sends nothing on a "no";
# - the desktop app's tests and clippy with warnings as errors, and `check`: a job
# run in the app's worker process, sealed in the app with a key that never leaves
# it, and verified, with no window.
@ -23,6 +25,7 @@ on:
- 'wai/wai-quantum-desktop/**'
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-cli/**'
- 'wai/wai-quantum-qpu/**'
- 'wai/wai-web/**'
- 'wai/wai-rs/**'
- 'rust-toolchain.toml'
@ -33,6 +36,7 @@ on:
- 'wai/wai-quantum-desktop/**'
- 'wai/wai-quantum/**'
- 'wai/wai-quantum-cli/**'
- 'wai/wai-quantum-qpu/**'
- 'wai/wai-web/**'
- 'wai/wai-rs/**'
- 'rust-toolchain.toml'
@ -100,6 +104,7 @@ jobs:
set -euo pipefail
npm ci --no-audit --no-fund
npx playwright install --with-deps chromium
node test/hardware.test.mjs
node build.mjs --pkg "$RUNNER_TEMP/pkg"
node test/studio.test.mjs
@ -132,6 +137,7 @@ jobs:
set -euo pipefail
nice -n 10 cargo test --locked --release 2>&1 | tee "$RUNNER_TEMP/test.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; }
grep -q '^test tests::a_run_on_a_provider_is_estimated_refused_until_confirmed_and_sealed_with_the_app_s_key \.\.\. ok$' "$RUNNER_TEMP/test.log" || { echo "::error::the provider run test did not run and pass"; exit 1; }
nice -n 10 cargo clippy --locked --release --all-targets -- -D warnings
nice -n 10 cargo build --locked --release
./target/release/wai-quantum-desktop check

View file

@ -167,8 +167,9 @@ jobs:
# signed in wasm32 under Node, its receipt checked there and a changed result
# refused, every method a tab holds run with no clock and no threads, a
# program's critique, equivalence check and variants, and its classical
# shortcuts with a contraction search. The count is the four tests above and
# its four.
# shortcuts with a contraction search, and the OpenQASM 3 conformance corpus
# (wai/quantum-conformance/qasm3/bundle.json) read and run in the page. The
# count is the four tests above and its five.
- name: The job engine in a page (wasm32 tests under Node)
shell: bash
working-directory: wai/wai-web
@ -176,22 +177,23 @@ jobs:
set -euo pipefail
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner \
cargo test --locked --target wasm32-unknown-unknown --features quantum_engine 2>&1 | tee "$RUNNER_TEMP/wai-web-engine-wasm.log"
grep -q "test result: ok. 8 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine-wasm.log"
grep -q "test result: ok. 9 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine-wasm.log"
# Its native half: the service's form and receipt, any angle on the state
# vector, what does not run saying why, plan, lint, draw and methods as JSON,
# critique, equivalence and variants as JSON, the classical shortcuts, the
# workbench run and sealed in the page, a program's history recorded and
# blamed there, the reader's rules checking a proposal, what is new in a
# program and in a workbench entry, the reader's taste learned there, the
# rewrites offered and explained there, convergence measured there, and a
# study run there.
# rewrites offered and explained there, convergence measured there, a
# study run there, and the reader's diagnostic codes and its whole
# language (a subroutine, an inverse, a power) answered there.
- name: The job engine in a page (native unit tests)
shell: bash
working-directory: wai/wai-web
run: |
set -euo pipefail
cargo test --locked --lib --features quantum_engine quantum_engine:: 2>&1 | tee "$RUNNER_TEMP/wai-web-engine.log"
grep -q "test result: ok. 14 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine.log"
grep -q "test result: ok. 15 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine.log"
# The crate's native unit tests, run on the host: the pure-Rust halves of the
# bindings, which wasm_tests does not run. They decode the committed integer
# payloads to their sidecars' SHA-256 (int_payload::tests) and check that the

View file

@ -2596,6 +2596,76 @@ wire format may change and such changes are recorded here.
- **`tools/ops-gpu-bench`** times each capability's decode with its graphs on the CPU and on each adapter, and reads the energy counters the host has.
- **CI:** `wai-ops-gpu.yml` runs the Metal and browser legs on the `macos` runner, and requires on a runner with no GPU that the device tests skip without `WAI_OPS_GPU_REQUIRE` and fail with it.
### wai-quantum: one OpenQASM 3 front end, every refusal and warning under a stable code
One reader, with the source position of everything it handles, for every surface: the job service, the command line, both MCP servers, the Studio, the desktop app, the edge component and the in-browser IDE.
- **Diagnostic codes** (`qasm::diag`): every refusal and warning the reader gives, and every construct it lowers, carries or keeps verbatim, has a code with its outcome (refused, lowered, carried, preserved, warned) and a one-line meaning. A diagnostic's text keeps the message people read and ends with its code (`line 3: unknown gate foo [Q103]`); as data it has its line, column and length. The table is `wai-quantum/docs/qasm-diagnostics.md`, which a test holds to the code's table, and is served by `wai-quantum diagnostics [--json]`, the `qasm_diagnostics` tool of both MCP servers and `engine_diagnostic_codes()` in the browser engine. A code never changes meaning once released. `lint` gives the code, the column and the length, and the warnings.
- **The language** (`qasm::lex`, `qasm::parse`, `qasm::elab`): `def` subroutines, inlined (classical arguments by value, qubits by reference, values returned, a returned measurement written into the caller's bit, bits of their own fed forward); `inv @`, `pow(k) @` and exact fractional powers, and `ctrl`/`negctrl` on any gate, user gates included, each lowered exactly and held to the matrix it must equal; `float` (16, 32 and 64 bits), `angle[n]` (held to n bits of a turn, so dyadic, with the rounding reported), `complex`, `uint`, `bool`, bit registers set by constants, arrays, casts and the built-in functions, computed with the crate's own functions; `while` (unrolled when its condition is known, run at most `@wai.max_iterations N` times on measured bits), `switch`, `break`, `continue`, `end`; conditions on measured bits with comparisons, `&&`, `||` and `!`, compiled to forward jumps and checked against every value of their bits; `output`, carried into the result; `delay`, `box`, `stretch`, `barrier` and gate durations, carried as timing and labelled in a result whose method applies no durations; `defcal`, `cal` and `defcalgrammar`, kept verbatim and never run; aliases, slices, index sets, physical qubits, `gphase`.
- **One grammar**: `quantum_toolchain::from_qasm` (the edge component's and the processor clients' import) reads through the same reader and applies the bit-exact circuit's restriction, refusing with the reader's codes (Q500 a non-dyadic angle, Q501 width, Q502 a dynamic program). A controlled gate the modifiers write stays one gate under all its controls, so `to_qasm`'s output reads back gate for gate. A new feature, `quantum_lang`, holds the reader without the lowerings.
- **A job's result** carries `outputs`, a `timing` label (`applied: false`, Q400) and the reader's `diagnostics`, each only when there is one, so a program with none has the result it always had.
- **Conformance** (`wai/quantum-conformance/qasm3/`): the specification's 21 example programs, unchanged, and 38 programs for the constructs with references, each with what the reader must do (an exact distribution or outcome, equivalence to a reference, or a code). `stdgates.qasm` holds every standard gate to its definition in `U` and `CX`. Run natively and under wasm32-wasip2 (`wai-quantum-lang.yml`) and in the browser build (`wai-web-wasm.yml`: wasm32 count 8 to 9, native `quantum_engine::` count 14 to 15).
- **Feature matrix**: `wai-quantum/docs/qasm-feature-matrix.md`, every construct of OpenQASM 3.1 with what the reader does with it and its code.
- **Changed refusals**, each now read: `inv @`, `pow @`, a control on `rz`, `while`, two registers and gate definitions in `from_qasm`; a loop's integer sharing a gate parameter's name; `^` between integers under `OPENQASM 3` (exclusive or). A bare radian number in `from_qasm` is now held to the reader's dyadic tolerance, where a number within 1e-9 of a dyadic angle was rounded to it.
### wai-quantum: the engine in editors, a language server, a formatter, a grammar and an extension
The engine where programs are written: any editor that speaks the language server protocol gets its diagnostics, its matrices and counts, and its runs.
- **The language service** (`engine::editor`), pure and with no protocol in it:
- diagnostics: the reader's refusal on its line (one function, `reader_diagnostic`, maps the reader's errors, so stable codes and exact spans plug in there), and the critique's smells on theirs, their kind as the code; a generator read at the given inputs, or 4;
- completion by what the cursor's place takes (qubits after a gate's name, values in its parameters, integers in an index, bits after `->`), the standard gates with placeholders, snippets, and sample programs in an empty file;
- hover: each standard gate's matrix written exactly, held entry by entry to the engine's own under every name the reader accepts; a user gate's matrix from the engine's matrices of its gates; each gate's cost as the engine counts it (two-qubit gates, T count, depth, Clifford), at the call's angles or generic ones, said which; a register's size;
- definitions, references, highlights and renames over an index of declarations and scopes that reads all of OpenQASM 3 and a half-typed program; an outline; folding (blocks, comments, held blocks);
- a formatter with one layout that checks it keeps every token, refuses a program whose brackets do not match, and is tested stable and meaning-preserving on a corpus in every part of the language.
- **The command line:** `wai-quantum lsp` serves it over standard input and output (UTF-16 or UTF-8 positions, as agreed), with Run, Plan, Critique, Shortcuts and Receipt as lenses run in-process and signed with the user's key; `wai-quantum fmt` formats, `--check`s and `--write`s.
- **A tree-sitter grammar** (`wai/tools/tree-sitter-openqasm`): all of OpenQASM 3.1 and OpenQASM 2's forms, its generated parser committed, queries for highlighting, scopes and folds; every program in the repository's conformance material and examples parses with no error.
- **The editor extension** (`wai/tools/wai-quantum-editor`): highlighting, the server started from a configurable path with install commands when it is missing, the commands' answers in an output panel, a receipt saved as JSON; packaged as a `.vsix`, not published. Configurations for editors configured in Lua, by `languages.toml`, and with WebAssembly extensions (built for `wasm32-wasip2`).
- **CI:** `wai-quantum-editors.yml` runs the service's and the server's tests on Linux and macOS, regenerates the grammar and holds it to the committed parser, parses and formats every program in the repository (formatting twice changes nothing, and the engine draws the same circuit), and packages and checks the extension.
### wai-quantum: bring your own quantum
People bring their own quantum hardware, provider accounts, on-premises devices or simulators; WAI gives the engine, the free job service and the verifier, and never pays for or resells hardware time. A run on a person's provider is planned, its cost shown before it is submitted, checked, and sealed like any other job.
- **The open provider protocol** (`extensions/quantum-providers.md`, `wai.quantum.provider/1`): JSON messages over HTTP or to a local process on its standard input and output; `describe` (native gates, connectivity, qubits, durations, a calibration snapshot id with optional per-qubit and per-pair errors, the billing unit and price or free), `estimate`, `submit`, `status`, `result` (counts keyed by classical bits, the highest first) and `cancel`; a program in the device's own gates, every angle an exact fraction of a turn, with its OpenQASM 3 beside it; error codes; what the receipt proves and what it does not.
- **wai-quantum-qpu:**
- `open_provider`: the client over either transport (a credential only over https or to this machine; in a process's environment, never its command line), and `OpenProvider`, a `Provider` over it;
- `reference`: the reference provider, the protocol around the exact simulator, served both ways (`wai-qpu reference`); free or priced per shot or job in exact decimals, with status polls, a readout error and a credential checked by its hash; the template to copy;
- `conformance`: the checker and its vectors (`conformance/provider-v1.json`), the reference's counts pinned; jobs run only when every one is free or the person allows the spend;
- `registry`: the person's list (`~/.config/wai/quantum-providers.json`), holding no credential; credentials in the keychain (the system `security` tool, or `secret-tool`), else a 0600 file, labelled so in every listing; environment variables still work;
- `job`: the dry run (the exact native compile, the check by simulation, the provider's estimate and billing unit), the refusal of a run that is not free (or of unknown cost) until it is confirmed, and the seal of its counts;
- `Provider` gains `details`, `estimate`, `submit_seeded` and `cancel`, each with a default.
- **The engine** (`engine::provider`): what a provider job may ask (counts of a program measured at the end, every angle dyadic), the measured result labelled `measured` with the circuit's exact distribution beside it (total variation distance, Hellinger fidelity, and what the shots alone leave), and its seal, which `job::verify` checks. `catalog::read` refuses a job that names a provider, so the job service never runs one on its own methods.
- **The command line:** `providers add|list|remove|describe|test|free|reference`, and `run --provider NAME [--dry-run] [--yes]` (a run that is not free exits 3 without `--yes`). The stdio MCP server gains `providers_list`, `provider_estimate` and `run_on_provider`; the last says it can spend the person's money and refuses without `confirm_cost: true` when the run is not free.
- **The desktop app:** `providers_list`, `provider_estimate` and `run_on_provider` commands; the Studio's **Your hardware** switch lists the providers, asks for the estimate and shows a dialog before a run that is not free. In a browser tab the switch says such runs go through the desktop app or the command line, since a tab must not hold a credential.
- **The job service** never routes to a provider and never takes a credential (its README says so; a job naming one is refused).
- **Docs:** `wai-quantum-qpu/bring-your-own-quantum.md` (adding a provider, the protocol, the reference as a template, what the receipt proves and what it does not, and what a person does to connect a real device).
- **CI:** `wai-quantum-qpu.yml` pins its three test binaries and the new tests by name, with a macOS leg for the keychain in a keychain file the test makes; `wai-quantum-cli.yml` follows the crate and pins the five end-to-end tests; `wai-quantum-studio.yml` runs the hardware switch's logic test. Nothing in them spends money.
### wai-quantum: Python and notebooks, in-process
WAI Quantum from Python and notebooks, with the same answers and receipts as every other surface: the shared job engine in the process, a client for the job service, and notebook widgets.
- **`wai/wai-quantum-py`** (Python module `wai_quantum`): a pyo3 extension module over the shared job engine, built by maturin into one abi3 wheel per platform, Python 3.9 on.
- The engine's functions: `run`, `plan`, `lint`, `draw` (text and SVG), `layout`, `critique`, `improve`, `shortcuts`, `equivalent`, `verify`, `seal`, `canonical`, `digest`, `methods`, `accuracy_classes` and `history`, and `bloch`: each qubit's reduced state from the engine's expectation values of X, Y and Z.
- Answers are plain dicts and lists. A job is the service's form, with helpers: `counts`, `probabilities()` (the sampled frequencies), `expectations`, `amplitudes`, `method`, `accuracy` and `receipt`.
- Values cross one to one with JSON's: NaN, infinities, integers past 64 bits and keys that are not strings are refused, never guessed at.
- A refusal raises `EngineError` with the engine's message and the line it names; its `code` is the reader's stable diagnostic code (`Q103`) when the refusal is the reader's.
- **Keys:** a result is sealed with the key the caller passes (`Key`), by default this user's key, which the command line also signs with. The demo key and the key files are the command line's, so the same key signs on both.
- **`Service`:** a client in the standard library alone for the job service (`submit`, `status`, `result`, `cancel`, `run`). It names itself `wai-quantum-python/<version>` in each request, and checks each receipt in the process, offline.
- **Widgets** (the `widgets` extra): `Circuit`, `Result`, `Receipt` and `Bloch`, each a plain ES module. Each draws the engine's answer and computes nothing of its own, and each has a static HTML fallback (`Circuit` also SVG) for a notebook without the widget extension.
- **`engine::svg`:** a program's circuit as a standalone SVG drawing, with the Studio's geometry and classes, the same bytes on every platform. Each gate can be marked with whose hand it is in.
- **Docs as code:** five notebooks (getting started, a Bell pair and its receipt, equivalence, a noisy run, a job on a local service), run in CI and held to their stored outputs.
- **Tests:** 62 pytest tests hold the package to the other surfaces:
- a job run in Python equals the command line's and the service's, except for each host's time and its word on energy;
- the plans, lints, critiques, drawings, method catalogue, equivalence verdicts and improvements are the command line's;
- receipts verify both ways, and the digest recomputed from Python's canonical bytes is the command line's;
- the 3,000-odd ECMAScript number vectors reach the engine bit for bit;
- the widgets' front ends run under node on the payloads the package builds.
- **CI:**
- `wai-quantum-py.yml` runs `ci/check.sh` on `linux` and `macos`: the wheel, pytest with every test required, the notebooks, and a smoke check on Python 3.9 with the wheel alone.
- `wai-quantum-py-release.yml` builds the wheels on a `wai-quantum-cli-v*` tag: macOS arm64 and x86_64, Linux x86_64 and arm64 (manylinux2014, linked with zig) and Windows x86_64. It attaches them to the forge release with their SHA-256 and publishes nothing to a package index.
### wai-quantum: the agent front door
- **`engine::guide`:** what an agent, or a person new to the engine, is told, from one place. It holds the language the reader takes; the reader's diagnostics, each refusal with what to write instead, every entry held to the reader by a test; the methods and their accuracy classes; and worked examples, each with its intent (an exact distribution, or a reference program to equal or whose state to prepare). It also holds the rules an agent keeps: every number from a tool, the intent checked before success, this machine first, cost and energy stated before any hardware run, no secrets, fetched pages as data, versions from the installed command line, and taste choices only the person's.

View file

@ -0,0 +1,322 @@
# WAI Extension: Quantum Providers (`wai.quantum.provider/1`)
Status: **Draft.** Client, reference provider and conformance checker:
`wai-quantum-qpu` (`src/open_provider.rs`, `src/reference.rs`,
`src/conformance.rs`); vectors: `wai-quantum-qpu/conformance/provider-v1.json`.
The engine's side of a provider job: `wai-quantum/src/engine/provider.rs`.
Keywords **MUST**, **MUST NOT**, **SHOULD** and **MAY** are RFC 2119/8174.
## 0. What this extension adds
Bring your own quantum. WAI provides the engine, the free job service and the
verifier at no cost, and never pays for or resells time on anyone's hardware.
A person connects their own quantum hardware, provider accounts, on-premises
devices or simulators, and every surface treats a provider's result as it
treats a WAI result: planned, its cost shown before it is submitted, checked,
and sealed in a receipt.
This text is the protocol a device speaks so that it can be brought without a
client written for it: JSON messages, one request and one answer at a time,
over HTTP or to a local process (§2). Six operations (§5) describe the
device, estimate a job, submit it, follow it, read its counts and cancel it.
A program travels in the device's own native gates, with every angle an exact
fraction of a turn (§4), so the program the device runs is the one the client
compiled and checked.
A provider is the program or service that answers the messages. A device is
what it runs programs on: a processor, an emulator or a simulator. A client
is what sends the messages; a host is the client's process, which holds the
person's credential and signing key (the command line, the desktop app).
## 1. Principles
1. **The credential stays with the person.** A host keeps a provider's
credential in the operating system's keychain (or, where there is none, a
file only its owner can read, labelled as such) and sends it only to that
provider (§2). A credential MUST NOT appear in a browser page, a log, a
receipt, a command line, an answer the host prints, or the hosted job
service. The job service never routes a job to a provider and never takes
a credential: a request that names a provider is refused there.
2. **Cost before submit.** A client MUST show a job's estimate (§5.2) before
it submits the job, and MUST NOT submit a job that is not free without the
person's explicit confirmation (§7). An unknown cost is not free.
3. **Exact programs.** A client compiles a circuit exactly to the device's
native gates and connectivity and checks the compiled program against the
circuit before sending it. Angles cross the wire exactly (§4).
4. **Measured, not recomputed.** A provider's counts are sealed as measured on
the device it names (§8). Nothing recomputes them.
## 2. Transports
Every message is a JSON object, and so is every answer. A client sends one
message and reads its answer before it sends the next on the same transport.
### 2.1 HTTP
A message is the body of a `POST` to the provider's endpoint (one URL for
every operation), with `Content-Type: application/json`. The answer is the
body of a `200` response, also for an answer with `"ok": false` (§3). Any
other status is a transport failure, not an answer.
A credential, when there is one, goes in `Authorization: Bearer <credential>`.
A client MUST NOT send a credential over `http://` to any host but this
machine (`localhost`, a loopback address); over the network it uses
`https://`.
### 2.2 A local process
The provider is a program the person names, with its arguments, started by
the client without a shell. Each message is one line of JSON on the program's
standard input; each answer is one line of JSON on its standard output, in
order. The program MUST write nothing else to its standard output. Its
standard error is its own log, and a client MAY discard it. The program runs
until its standard input ends.
A credential, when there is one, is in the program's environment as
`WAI_PROVIDER_CREDENTIAL`, never on its command line. A client SHOULD NOT
pass the program another provider's credential.
## 3. Messages and errors
Every message carries:
| field | |
|---|---|
| `protocol` | `"wai.quantum.provider/1"` |
| `op` | `describe`, `estimate`, `submit`, `status`, `result` or `cancel` |
| `settings` | optional: the person's non-secret settings for this provider (a device name, a region), passed as they are |
and the operation's own fields (§5). Every answer carries `"ok": true` and
the operation's fields, or:
```json
{ "ok": false, "error": { "code": "not_found", "message": "no job j-17" } }
```
The `message` is for a person; it MUST NOT quote a credential. The codes:
| code | when |
|---|---|
| `invalid` | the message is malformed: a field missing or of the wrong type, a program that does not read (§4), zero shots, a message without `protocol` |
| `unsupported` | an operation the provider does not offer (`cancel`, `estimate`), or an `op` it does not know |
| `unsupported_version` | a `protocol` other than this one |
| `unauthorized` | no credential, or one the provider does not accept |
| `not_found` | a job the provider does not hold |
| `refused` | a well-formed program the device cannot run: a gate outside its basis, a qubit it does not have, a pair it does not connect, too many shots; or a result asked of a job that has not finished or was cancelled |
| `quota` | the person's account cannot pay for or schedule the job |
| `unavailable` | the device is offline or in maintenance |
| `internal` | anything else |
## 4. The program
`estimate` and `submit` carry a `program` in the device's own gates, on its
physical qubits:
```json
{
"format": "wai.native/1",
"basis": "rzsxxcz",
"bits": 2,
"gates": [
{ "gate": "rz", "qubits": [0], "turns": [[1, 2]] },
{ "gate": "sx", "qubits": [0] },
{ "gate": "cz", "qubits": [0, 1] }
],
"measure": [{ "qubit": 0, "bit": 0 }, { "qubit": 1, "bit": 1 }],
"qasm3": "OPENQASM 3.0; …"
}
```
- `basis` is one of three gate sets, and every gate is of it:
| basis | gates |
|---|---|
| `rzsxxcz` | `rz(t)`, `sx`, `x`, `cz` |
| `prxcz` | `prx(t, p)`, `cz` |
| `gpizz` | `gpi(p)`, `gpi2(p)`, `zz(t)` |
- An angle is a fraction of a full turn, `[m, k]` meaning `m/2^k` of 2π, with
`0 ≤ m < 2^k` and `k ≤ 32`, so every angle crosses the wire exactly. The
gates, as operators on the named qubits, up to a global phase:
- `rz(t)` = exp(−iπt·Z); `sx` = √X; `x` = X; `cz` = diag(1, 1, 1, −1);
- `prx(t, p)` = Rz(2πp)·Rx(2πt)·Rz(−2πp);
- `gpi(p)` = Rz(2πp)·X·Rz(−2πp); `gpi2(p)` = Rz(2πp)·Rx(π/2)·Rz(−2πp);
- `zz(t)` = exp(−iπt·Z⊗Z).
- `measure` gives, for each classical bit `0 … bits − 1`, the physical qubit it
reads at the end of the program. Every bit reads exactly one qubit, and no
qubit is read twice.
- `qasm3` is the same program as OpenQASM 3 on physical qubits (`$p`), the
`prxcz` and `gpizz` gates defined in it from the standard gates, for a
provider that reads OpenQASM. It is informative: where the two differ, the
`gates` are the program.
A provider MUST refuse (`invalid`) a program of another `format`, a gate
outside its `basis`, an angle out of range, or a bit that reads no qubit; and
(`refused`) a program in a basis its device does not run, or on qubits or
pairs it does not have.
## 5. Operations
### 5.1 `describe`
No fields. The answer describes the device:
| field | |
|---|---|
| `protocol` | `"wai.quantum.provider/1"` |
| `provider`, `device` | the provider's name for itself, and the device's |
| `provenance` | `silicon` (a processor), `emulator`, or `simulator`; with `simulator` (or `emulator`) a sentence naming what it is |
| `qubits` | the device's qubit count |
| `native_gates` | the gates it runs; a client compiles to the first of the bases of §4 whose every gate is listed |
| `connectivity` | `"all"`, or the pairs `[a, b]` a two-qubit gate may act on, in either order |
| `durations` | optional: gate and measurement durations, with their unit |
| `calibration` | `{"id"}`, the calibration snapshot the device is under; optionally `qubits` (`{"qubit", "readout_error", "error_1q", "t1_us", "t2_us"}`) and `edges` (`{"qubits": [a, b], "error"}`), each error from 0 to 1 |
| `billing` | `{"free": true}`, or `{"unit", "price", "currency"}`: the billing unit (`shot`, `job`, `second`, …), the price of one as a decimal string (`"0.0003"`), and the currency or credit it is in; optionally `free_tier`, the free use offered and its limits, as the provider states them |
| `max_shots` | optional: the most shots a job may ask |
| `seeded` | optional: `true` when a job's `seed` makes its counts reproducible (a simulator) |
A client MUST NOT take a device to be free unless `billing` says
`"free": true`, or an estimate says so (§5.2).
### 5.2 `estimate`
Fields: `program` (§4), `shots`. The answer, before anything is submitted:
| field | |
|---|---|
| `free` | whether this job is free |
| `amount`, `currency` | when it is not: the cost, the amount as a decimal string |
| `unit` | optional: the billing unit the cost counts |
| `seconds` | optional: how long it is expected to take, queue included |
| `note` | optional |
A provider that cannot estimate answers `unsupported`; the client then treats
the cost as unknown (§7).
### 5.3 `submit`
Fields: `program`, `shots`, and optionally `seed` (a seeded simulator uses
it; a device ignores it). The answer's `job` is the job's id.
### 5.4 `status`
Field: `job`. The answer's `status` is `queued`, `running`, `done`, `failed`
or `cancelled`, with an optional `reason`.
### 5.5 `result`
Field: `job`. The answer, once the job is `done`:
| field | |
|---|---|
| `counts` | an object from outcomes to counts (§6), summing to the shots |
| `shots` | the shots |
| `calibration` | optional: the calibration snapshot the job ran under, when it is not the one `describe` names |
| `charged` | optional: `{"amount", "currency"}`, what the job cost |
| `energy` | optional: `{"kwh"}`, the provider's own figure for the job's energy |
A `result` asked twice of the same job MUST give the same counts.
### 5.6 `cancel`
Field: `job`. The answer's `status` is `cancelled`, or `done` for a job that
had already finished. A provider that cannot cancel answers `unsupported`.
## 6. Counts and bit order
An outcome is a string of `bits` characters, `0` or `1`, the highest bit
first: its last character is bit 0. `"01"` is bit 0 read as 1 and bit 1 read
as 0. This is OpenQASM's order, and the job service's.
The client compiles each logical qubit `i` of the person's circuit to end on
the physical qubit that bit `i` measures, and reads the counts back onto the
program's own classical bits. The first job on a new provider SHOULD be the
probe (an `X` on qubit 0, every qubit measured), whose counts must all land
on `…01`; the conformance vectors begin with it (§9).
## 7. Cost and confirmation
A job is free when the provider's estimate says `"free": true`, or, for a
provider that gives no estimate, when its `billing` says so. Every other job,
an unknown cost included, is not free. A client:
- MUST show the estimate (or that the cost is unknown) before submitting;
- MUST NOT submit a job that is not free without the person's explicit
confirmation for that run: a command-line flag (`wai-quantum run --provider
NAME --yes`), an agent tool's argument the agent sets only after the person
agreed (`run_on_provider` with `confirm_cost: true`), or a dialog (the
desktop app);
- MUST NOT hold a standing confirmation across runs.
A tool that can submit a job that is not free MUST say in its description
that it can spend the person's money.
## 8. The receipt
The host seals each provider job as the job service seals its own: an Ed25519
signature, by the host's key, over the request as submitted and the result
as returned, each in its RFC 8785 canonical text, verified by the same
verifier. The result's `method` is `provider` and its accuracy class is
`measured`. It carries:
- `provider`: the person's name for it, its kind, its protocol, the device,
its provenance, and the BLAKE3 hash of its `describe` answer;
- `calibration`: the calibration snapshot the job ran under;
- `compiled`: the basis, the BLAKE3 hash of the compiled program's OpenQASM 3
text, its gate and swap counts, its initial and final layout, and the check
it passed before it was sent;
- `job`: the provider's job id and the status checks before it finished;
- `counts`, keyed by the program's classical bits (§6);
- `ideal`: the circuit's exact distribution set beside the counts (up to 22
qubits): the total variation distance, the Hellinger fidelity, and the
distance the shots alone would leave on average;
- `cost`: the estimate shown before submitting, what the provider says it
charged, and whether the person confirmed a run that was not free;
- `energy`: unmetered here, with the provider's own figure, labelled as its
own, when it gives one.
What the receipt proves: that this result, with these counts, is what the
signer's host received for this request from the provider and device it
names, under the calibration snapshot named, for the compiled program whose
hash it carries; and that the host compiled that program exactly and checked
it against the circuit before sending it. What it does not prove: that the
device ran that program, or ran anything. The counts are the provider's word,
and they are not reproducible by recomputation: the same job run again gives
other counts. The `ideal` distance says how far they are from what the
circuit gives; a distance well above the sampling distance is the device's
error, or a program the device did not run as sent.
## 9. Conformance
`wai-quantum-qpu/conformance/provider-v1.json` holds the vectors:
- **messages**, sent as they are: an unknown operation (`unsupported`),
another version (`unsupported_version`), a message without its protocol
(`invalid`), a job that does not exist (`not_found`), malformed programs
(`invalid`);
- **programs**, each compiled to the device, estimated, run with seed 1, and
checked by its counts: the probe, bits named out of order, a phase only
interference shows, a phase kicked back by two quarter-turn controlled
phases, a Bell pair, and a triangle of two-qubit gates that needs routing
on a line. The thresholds leave room for a device's noise;
- **reference**: the counts the reference provider gives for each program at
seed 1, so its determinism is held on every platform.
The checker (`wai-quantum providers test NAME`) also checks the `describe`
answer in full, that zero shots are `invalid`, and that a program in another
basis is refused. It runs the programs only when every estimate says free,
or the person allows the spend (`--yes`).
The reference provider (`wai-quantum providers reference --stdio`, or
`--http ADDR`) is this protocol around WAI's exact simulator. It is the
provider the tests run against over both transports, and the template to
copy: a provider for any device is the reference with its simulation replaced
by the device.
## 10. Versioning
`wai.quantum.provider/1` is this text. A later version is a new `protocol`
string; a provider that speaks only this one answers `unsupported_version` to
another. Fields marked optional may be added to answers without a new
version, and a client MUST ignore fields it does not know.

View file

@ -16,6 +16,9 @@ Four trees, each a pinned corpus:
| `reject/` + `reject/reject.json` | both | payloads a conforming reader refuses, each with the class it is refused as: `malformed`, `contract` or `invalid` (extension §2) |
| `accept/` + `accept/accept.json` | both | payloads a conforming reader accepts, at the edges of the same rules, each with how it reads: capability, `n_qubits`, op count, circuit hash and measurement (extension §2) |
The OpenQASM 3 reader's corpus, programs with what the reader must do with
each, is `qasm3/`, with a README of its own.
An entry with no `capability` is a `wai.quantum.circuit` vector. The trees are the
one reference all three surfaces are checked against, rather than being checked
against each other.

View file

@ -0,0 +1,52 @@
# OpenQASM 3 conformance
OpenQASM 3 programs, each with what the reader (`wai_quantum::qasm`) must do
with it: read it to an exact result, read it to the unitary of a reference
program, or refuse it with a given diagnostic code.
| Path | What |
|---|---|
| `spec/` | The OpenQASM 3 specification's example programs, unchanged (Apache-2.0, as the specification is published). Those that run are held to their results; those that cannot be run exactly are held to the code the reader refuses them with, which names the first construct it cannot hold (an `extern`, `durationof`, a `while` on measured bits with no bound) or the example's own error (`arrays.qasm` indexes past its array; `cphase.qasm` and `defcal.qasm` are fragments that declare no qubits). |
| `lang/` | A program for each construct the reader lowers, carries, preserves or refuses, and `*.ref.qasm` references written without the construct. `stdgates.qasm` applies every standard gate once; its reference writes each in `U` and `CX` alone. |
| `manifest.json` | Each case: its file, what it covers, and its expectations (the manifest's `expectations` says what each kind means). |
| `bundle.json` | The manifest's cases with each program's text inline, for builds that read no files. |
An expectation is one or more of: `refused` (a code), `warns` (the codes of
every warning and note, sorted), `deterministic` (every shot's outcome),
`distribution` (exact outcome probabilities, within 1e-9), `expectations`
(exact Pauli expectation values), `equivalent` (the reference's unitary, up to a
global phase, within 1e-9), `exact` (the bit-exact circuit takes it, or refuses
it with a code), `outputs`, `timing` and `calibrations`.
## Native
```sh
cd wai/wai-quantum
cargo test --release --features full --test qasm3_conformance
```
Two tests: every case against its expectations, and the bundle held equal to
the files. After changing a program or the manifest, write the bundle:
```sh
WAI_QASM3_BUNDLE_WRITE=1 cargo test --release --features full --test qasm3_conformance the_bundle_is_the_corpus
```
## WASI (the edge)
```sh
CARGO_TARGET_WASM32_WASIP2_RUNNER=wasmtime \
cargo test --target wasm32-wasip2 --features full --test qasm3_conformance
```
The cases come from the bundle compiled into the test.
## Browser
wai-web's wasm32 tests (`--features quantum_engine`, under
`wasm-bindgen-test-runner`) read every case of the bundle through the page's
engine: each refusal's code, each program's warnings, and each program with one
outcome run to it.
CI: `wai-quantum-lang.yml` (native and wasm32-wasip2) and `wai-web-wasm.yml`
(the browser build).

View file

@ -0,0 +1,525 @@
{
"format": "wai.qasm3-conformance-bundle/1",
"about": "manifest.json's cases with each program's text inline, for builds that read no files (the browser, the edge); a test holds it equal to the files",
"cases": [
{
"file": "spec/adder.qasm",
"qasm": "/*\n * quantum ripple-carry adder\n * Cuccaro et al, quant-ph/0410184\n */\ninclude \"stdgates.inc\";\n\ngate majority a, b, c {\n cx c, b;\n cx c, a;\n ccx a, b, c;\n}\n\ngate unmaj a, b, c {\n ccx a, b, c;\n cx c, a;\n cx a, b;\n}\n\nqubit[1] cin;\nqubit[4] a;\nqubit[4] b;\nqubit[1] cout;\nbit[5] ans;\nuint[4] a_in = 1; // a = 0001\nuint[4] b_in = 15; // b = 1111\n// initialize qubits\nreset cin;\nreset a;\nreset b;\nreset cout;\n\n// set input states\nfor uint i in [0: 3] {\n if(bool(a_in[i])) x a[i];\n if(bool(b_in[i])) x b[i];\n}\n// add a to b, storing result in b\nmajority cin[0], b[0], a[0];\nfor uint i in [0: 2] { majority a[i], b[i + 1], a[i + 1]; }\ncx a[3], cout[0];\nfor uint i in [2: -1: 0] { unmaj a[i],b[i+1],a[i+1]; }\nunmaj cin[0], b[0], a[0];\nmeasure b[0:3] -> ans[0:3];\nmeasure cout[0] -> ans[4];\n",
"expect": {
"deterministic": "10000"
}
},
{
"file": "spec/alignment.qasm",
"qasm": "/* CPMG XY4 decoupling\n * This example demonstrates the use of stretch to\n * specify design intent on gate alignment, without\n * being tied to physical qubits and gates.\n*/\ninclude \"stdgates.inc\";\n\nstretch g;\n\nqubit[3] q;\nbarrier q;\ncx q[0], q[1];\ndelay[g] q[2];\nU(pi/4, 0, pi/2) q[2];\ndelay[2*g] q[2];\nbarrier q;\n",
"expect": {
"warns": [
"Q403"
],
"timing": true,
"distribution": {
"000": 0.8535533905932737,
"100": 0.14644660940672624
}
}
},
{
"file": "spec/arrays.qasm",
"qasm": "OPENQASM 3.0; // Version statement is optional\n\n// ==================\n// Array declarations\n// ==================\n\n// Declare 'my_ints' to be a 1D array of 8-bit signed integers, where the index\n// runs from 0 to 15.\narray[int[8], 16] my_ints;\n\n// Declare 'my_doubles' to be a 2D array of double-precision floating-point\n// values, where the first index runs from 0 to 7 and the second from 0 to 3.\narray[float[64], 8, 4] my_doubles;\n\n// Initialise a 1D array with fixed values.\narray[uint[32], 4] my_defined_uints = {5, 6, 7, 8};\n\n// Initialise a 2D array with fixed values.\narray[float[32], 4, 2] my_defined_floats = {\n {0.5, 0.5},\n {1.0, 2.0},\n {-0.4, 0.7},\n {1.3, -2.1e-2}\n};\n\n// Initialise an array using a row of another array. Note that this copies the\n// data; changes to the values in `my_defined_float_row` will not affect\n// `my_defined_floats`.\narray[float[32], 2] my_defined_float_row = my_defined_floats[0];\n\n// Arrays can be defined in terms of compile-time constant dimension sizes, and\n// can use expressions in the slots of their initialisers.\nconst uint[8] DIM_SIZE = 2;\narray[int[8], DIM_SIZE, DIM_SIZE] all_ones = {{2+3, 4-1}, {3+8, 12-4}};\n\n\n// =============================\n// Array indexing and operations\n// =============================\n\n// Use `[]` notation to access array elements. The dimensions are\n// comma-separated.\nuint[8] a = my_defined_uints[0];\nfloat[32] b = my_defined_floats[2, 1];\n\n// The same notation is used in simple assignments.\nmy_defined_uints[1] = 5;\nmy_defined_floats[3, 0] = -0.45;\n\n// In both, the indices can be (non-constant) expressions.\nmy_defined_uints[a - 1] = a + 1;\n\n// Assignments can also be done using an array, or array slice, on the left-hand\n// side, provided the right-hand side evaluates to an array of exactly the same\n// shape. The assignment is done by copy, so subsequent changes to either of\n// the two parts will not affect the other.\nmy_defined_floats[2] = my_defined_float_row;\n\n// You can also use the slice notation to set elements. Beware that it is\n// generally a logical error to read and write from overlapping slices at the\n// same time, and that OpenQASM 3 makes no guarantees about which order the data\n// will be read from and written to. You might clobber data if you overlap.\nmy_defined_floats[0:1] = my_defined_floats[2:3];\n\n// The ``sizeof`` operator returns the size of an array's dimension.\nconst uint[32] dimension = sizeof(my_defined_uints); // assigns 4.\n\n// The ``sizeof`` operator can also be used in two-argument form, where the\n// second argument is the index of the dimension, counting from 0.\nconst uint[32] first_dimension = sizeof(my_doubles, 0); // returns 8\nconst uint[32] second_dimension = sizeof(my_doubles, 1); // returns 4\n\n// If the second argument is omitted from ``sizeof`` and the given array is\n// multi-dimensional, it defaults to returning the first dimension, so\n// ``sizeof(my_array) == sizeof(my_array, 0)`` in all circumstances.\nconst uint[32] first_dimension = sizeof(my_doubles); // still 8.\n\n\n// =====================\n// Arrays in subroutines\n// =====================\n\n// Array arguments have a mandatory ``readonly`` or ``mutable`` specifier when\n// defined in a subroutine argument list. This is because arrays are passed to\n// subroutines by reference, not by value, so modifications will propagate back\n// to the original data. Such modifications are only allowed for ``mutable``\n// references, not ``readonly``.\ndef copy_3_bytes(readonly array[uint[8], 3] in_array, mutable array[uint[8], 3] out_array) {\n // Within this block, ``in_array`` can be read from, but not written to,\n // whereas ``out_array`` can be both read from and written to.\n}\n\n// When specifying array subroutine parameters, there is a second format where\n// the sizes of the dimensions are not given explicitly, only the number of\n// dimensions. This is where the ``sizeof`` operator is most useful. In these\n// cases, `sizeof` is _not_ a compile-time constant.\ndef multi_dimensional_input(readonly array[int[32], #dim=3] my_array) {\n uint[32] dimension_0 = sizeof(my_array, 0);\n uint[32] dimension_1 = sizeof(my_array, 1);\n uint[32] dimension_2 = sizeof(my_array, 2);\n}\n",
"expect": {
"refused": "Q107"
}
},
{
"file": "spec/cphase.qasm",
"qasm": "gate cphase(θ) a, b\n{\n U(0, 0, θ / 2) a;\n CX a, b;\n U(0, 0, -θ / 2) b;\n CX a, b;\n U(0, 0, θ / 2) b;\n}\ncphase(π / 2) q[0], q[1];\n",
"expect": {
"refused": "Q102"
}
},
{
"file": "spec/dd.qasm",
"qasm": "/* CPMG XY4 decoupling w/ boxing\n * This example demonstrates the use of referential delays\n * and time alignment.\n*/\ninclude \"stdgates.inc\";\n\nstretch a;\nduration start_stretch = -0.5 * durationof({x $0;}) + a;\nduration middle_stretch = -0.5 * durationof({x $0;}) - 5 * durationof({y $0;}) + a;\nduration end_stretch = -0.5 * durationof({y $0;}) + a;\n\nbox {\n delay[start_stretch] $0;\n x $0;\n delay[middle_stretch] $0;\n y $0;\n delay[middle_stretch] $0;\n x $0;\n delay[middle_stretch] $0;\n y $0;\n delay[end_stretch] $0;\n\n cx $2, $3;\n cx $1, $2;\n u $3;\n}\n",
"expect": {
"refused": "Q405"
}
},
{
"file": "spec/defcal.qasm",
"qasm": "defcalgrammar \"openpulse\";\n\ndefcal x $0 {\n play drive($0), gaussian(...);\n}\n\ndefcal x $1 {\n play drive($1), gaussian(...);\n}\n\ndefcal rz(angle[20] theta) q {\n shift_phase drive(q), -theta;\n}\n\ndefcal measure $0 -> bit {\n complex[int[24]] iq;\n bit state;\n complex[int[12]] k0[1024] = [i0 + q0*j, i1 + q1*j, i2 + q2*j, ...];\n play measure($0), flat_top_gaussian(...);\n iq = capture acquire($0), 2048, extern(k0);\n return threshold(iq, 1234);\n}\n\ndefcal zx90_ix $0, $1 {\n play drive($0, \"cr1\"), flat_top_gaussian(...); // uses a non-default\n // frame labeled \"cr1\"\n}\n\ndefcal cx $0, $1 {\n zx90_ix $0, $1;\n x $0;\n shift_phase drive($0, \"cr1\");\n zx90_ix $0, $1;\n x $0;\n x $1;\n}\n",
"expect": {
"refused": "Q100"
}
},
{
"file": "spec/gateteleport.qasm",
"qasm": "// Repetition code gate teleportation\ninclude \"stdgates.inc\";\n\n// declarations\nconst int[32] n = 3;\nextern vote(bit[n]) -> bit;\n\ndef logical_meas(qubit[3] d) -> bit {\n bit[3] c;\n bit r;\n measure d -> c;\n r = vote(c);\n return r;\n}\n\nqubit[3] q;\nqubit[3] a;\nbit r;\n\n// prep magic state\nrz(pi/4) a;\n\n// entangle two logical registers\ncx q, a;\n\n// measure out the ancilla\nr = logical_meas(a);\n\n// if we get a logical |1> then we need to apply a logical Z correction\nif (r == 1) z q;\n",
"expect": {
"refused": "Q125"
}
},
{
"file": "spec/inverseqft1.qasm",
"qasm": "// QFT and measure, version 1\ninclude \"stdgates.inc\";\n\nqubit[4] q;\nbit[4] c;\nreset q;\nh q;\nbarrier q;\nh q[0];\nc[0] = measure q[0];\nif(int[4](c) == 1) { rz(pi / 2) q[1]; }\nh q[1];\nc[1] = measure q[1];\nif(int[4](c) == 1){ rz(pi / 4) q[2]; }\nif(int[4](c) == 2){ rz(pi / 2) q[2]; }\nif(int[4](c) == 3){ rz(pi / 2 + pi / 4) q[2]; }\nh q[2];\nc[2] = measure q[2];\nif(int[4](c) == 1) rz(pi / 8) q[3];\nif(int[4](c) == 2) rz(pi / 4) q[3];\nif(int[4](c) == 3) rz(pi/4+pi/8) q[3];\nif(int[4](c) == 4) rz(pi / 2) q[3];\nif(int[4](c) == 5) rz(pi / 2 + pi / 8) q[3];\nif(int[4](c) == 6) rz(pi / 2+ pi / 4) q[3];\nif(int[4](c) == 7) rz(pi / 2 + pi / 4 + pi / 8) q[3];\nh q[3];\nc[3] = measure q[3];\n",
"expect": {
"deterministic": "0000"
}
},
{
"file": "spec/inverseqft2.qasm",
"qasm": "// QFT and measure, version 2\ninclude \"stdgates.inc\";\n\nqubit[4] q;\nbit c0;\nbit c1;\nbit c2;\nbit c3;\n\nreset q;\nh q;\nbarrier q;\nh q[0];\nmeasure q[0] -> c0;\nif(c0 == 1) { rz(pi / 2) q[1]; }\nh q[1];\nmeasure q[1] -> c1;\nif(c0==1) { rz(pi / 4) q[2]; }\nif(c1==1) { rz(pi / 2) q[2]; }\nh q[2];\nmeasure q[2] -> c2;\nif(c0 == 1) { rz(pi / 8) q[3]; }\nif(c1 == 1) { rz(pi / 4) q[3]; }\nif(c2 == 1) { rz(pi / 2) q[3]; }\nh q[3];\nmeasure q[3] -> c3;\n",
"expect": {
"deterministic": "0000"
}
},
{
"file": "spec/ipe.qasm",
"qasm": "/*\n * Iterative phase estimation\n */\ninclude \"stdgates.inc\";\n\nconst int[32] n = 10; // number of iterations\nconst float[32] theta = 3 * pi / 8; // phase angle on target qubit\n\nqubit q; // phase estimation qubit\nqubit r; // target qubit for the controlled-unitary gate\nangle[n] c; // phase estimation bits\n\n// initialize\nreset q;\nreset r;\n\n// prepare uniform superposition of eigenvectors of phase\nh r;\n\n// iterative phase estimation loop\nuint[n] power = 1;\nfor uint i in [0: n - 1] { // implicitly cast val to int\n reset q;\n h q;\n ctrl @ pow(power) @ phase(theta) q, r;\n inv @ phase(c) q;\n h q;\n measure q -> c[0];\n // newest measurement outcome is associated to a pi/2 phase shift\n // in the next iteration, so shift all bits of c left\n c <<= 1;\n power <<= 1;\n}\n\n// Now c contains the n-bit estimate of phi in the\n// eigenvalue e^{i*phi} and qreg r is projected to an\n// approximate eigenstate of the phase gate.\n",
"expect": {
"refused": "Q128"
}
},
{
"file": "spec/msd.qasm",
"qasm": "/*\n * Magic state distillation and computation\n */\ninclude \"stdgates.inc\";\n\nconst int[32] buffer_size = 6; // size of magic state buffer\n\n// Y-basis measurement\ndef ymeasure(qubit q) -> bit {\n s q;\n h q;\n return measure q;\n}\n\n/*\n * Distillation subroutine takes 10 |H> magic states\n * and 3 scratch qubits that will be reinitialized.\n * The first two input magic states are the outputs.\n * The subroutine returns a success bit that is true\n * on success and false otherwise (see arXiv:1811.00566).\n */\ndef distill(qubit[10] magic, qubit[3] scratch) -> bool {\n bit temp;\n bit[3] checks;\n // Encode two magic states in the [[4,2,2]] code\n reset scratch[0: 1];\n h scratch[1];\n cx scratch[1], magic[0];\n cx magic[1], scratch[0];\n cx magic[0], scratch[0];\n cx scratch[1], magic[1];\n // Body of distillation circuit\n cy magic[2], scratch[0];\n h magic[1];\n temp = ymeasure(magic[2]);\n if(temp == 1) { ry(-pi / 2) scratch[0]; }\n reset scratch[2];\n h scratch[2];\n cz scratch[2], scratch[0];\n cy magic[3], scratch[0];\n temp = ymeasure(magic[3]);\n if(temp==0) { ry(pi / 2) scratch[0]; }\n h scratch[0];\n s scratch[0];\n cy magic[4], scratch[1];\n temp = ymeasure(magic[4]);\n if(temp==1) { ry(-pi / 2) scratch[1]; }\n cz scratch[3], scratch[2];\n cy magic[5], scratch[1];\n temp = ymeasure(magic[5]);\n if(temp==0) { ry(pi / 2) scratch[1]; }\n cy scratch[0], magic[1];\n inv @ s scratch[1];\n cz scratch[0], scratch[1];\n h scratch[0];\n cy scratch[1], magic[1];\n cy magic[6], scratch[0];\n temp = ymeasure(magic[6]);\n if(temp == 1) { ry(-pi / 2) scratch[0]; }\n cz scratch[2], scratch[1];\n cz scratch[2], scratch[0];\n cy magic[7], scratch[0];\n temp = ymeasure(magic[7]);\n if(temp == 0) ry(pi / 2) scratch[0];\n cy magic[8], scratch[1];\n temp = ymeasure(magic[8]);\n if(temp==1) { ry(-pi / 2) scratch[1]; }\n cz scratch[2], scratch[1];\n cy magic[9], scratch[1];\n temp = ymeasure(magic[9]);\n if(temp == 0) { ry(pi / 2) scratch[1]; }\n h scratch[2];\n // Decode [[4,2,2]] code\n cx magic[0], scratch[0];\n cx scratch[1], magic[1];\n cx magic[1], scratch[0];\n cx scratch[1], magic[0];\n h scratch[1];\n checks = measure scratch;\n success = ~(bool(checks[0]) | bool(checks[1]) | bool(checks[2]));\n return success;\n}\n\n// Repeat level-0 distillation until success\ndef rus_level_0(qubit[10] magic, qubit[3] scratch) {\n bool success;\n while(~success) {\n reset magic;\n ry(pi / 4) magic;\n success = distill(magic, scratch);\n }\n}\n\n/*\n * Run two levels of 10:2 magic state distillation.\n * Both levels have two distillations running in parallel.\n * The output pairs from the first level are separated and\n * input to different second levels distillation circuits\n * because a failure in a first level circuit can lead to\n * errors on both outputs.\n * Put the requested even number of copies into the buffer.\n */\ndef distill_and_buffer(int[32] num, qubit[33] work, qubit[buffer_size] buffer) {\n int[32] index;\n bit success_0;\n bit success_1;\n let magic_lvl0 = work[0: 9];\n let magic_lvl1_0 = work[10: 19];\n let magic_lvl1_1 = work[20: 29];\n let scratch = work[30: 32];\n\n // Run first-level circuits until 10 successes,\n // storing the outputs for use in the second level\n for uint i in [0: 9] {\n rus_level_0 magic_lvl0, scratch;\n swap magic_lvl0[0], magic_lvl1_0[i];\n swap magic_lvl0[1], magic_lvl1_1[i];\n }\n\n // Run two second level circuits simultaneously\n success_0 = distill(magic_lvl1_0, scratch);\n success_1 = distill(magic_lvl1_1, scratch);\n\n // Move usable magic states into the buffer register\n if(success_0 && index < buffer_size) {\n swap magic_lvl1_0[0: 1], buffer[index: index + 1];\n index += 2;\n }\n if(success_1 && index < buffer_size) {\n swap magic_lvl1_1[0: 1], buffer[index: index + 1];\n index += 2;\n }\n}\n\n// Apply Ry(pi/4) to a qubit by consuming a magic state\n// from the magic state buffer at address \"addr\"\ndef Ty(int[32] addr, qubit q, qubit[buffer_size] buffer) {\n bit outcome;\n cy buffer[addr], q;\n outcome = ymeasure(buffer[addr]);\n if(outcome == 1) ry(pi / 2) q;\n}\n\nqubit[33] workspace;\nqubit[buffer_size] buffer;\n\nqubit[5] q;\nbit[5] c;\nint[32] address;\n\n// initialize\nreset workspace;\nreset buffer;\nreset q;\n\ndistill_and_buffer(buffer_size) workspace, buffer;\n\n// Consume magic states to apply some gates ...\nh q[0];\ncx q[0], q[1];\nTy(address) q[0], buffer;\naddress += 1;\ncx q[0], q[1];\nTy(address) q[1], buffer;\naddress += 1;\n\n// In principle each Ty gate can execute as soon as the magic\n// state is available at the address in the buffer register.\n\n// We can continue alternating state distillation and computation\n// to refill and empty a circular buffer.\n",
"expect": {
"refused": "Q107"
}
},
{
"file": "spec/qec.qasm",
"qasm": "// Repetition code syndrome measurement\ninclude \"stdgates.inc\";\n\nqubit[3] q;\nqubit[2] a;\nbit[3] c;\nbit[2] syn;\n\ndef syndrome(qubit[3] d, qubit[2] a) -> bit[2] {\n bit[2] b;\n cx d[0], a[0];\n cx d[1], a[0];\n cx d[1], a[1];\n cx d[2], a[1];\n measure a -> b;\n return b;\n}\nreset q;\nreset a;\nx q[0]; // insert an error\nbarrier q;\nsyn = syndrome(q, a);\nif(int[2](syn)==1) x q[0];\nif(int[2](syn)==2) x q[2];\nif(int[2](syn)==3) x q[1];\nc = measure q;\n",
"expect": {
"deterministic": "01000"
}
},
{
"file": "spec/qft.qasm",
"qasm": "// quantum Fourier transform\ninclude \"stdgates.inc\";\nqubit[4] q;\nbit[4] c;\nreset q;\nx q[0];\nx q[2];\nbarrier q;\nh q[0];\ncphase(pi / 2) q[1], q[0];\nh q[1];\ncphase(pi / 4) q[2], q[0];\ncphase(pi / 2) q[2], q[1];\nh q[2];\ncphase(pi / 8) q[3], q[0];\ncphase(pi / 4) q[3], q[1];\ncphase(pi / 2) q[3], q[2];\nh q[3];\nc = measure q;\n",
"expect": {
"distribution": {
"0000": 0.0625,
"0001": 0.0625,
"0010": 0.0625,
"0011": 0.0625,
"0100": 0.0625,
"0101": 0.0625,
"0110": 0.0625,
"0111": 0.0625,
"1000": 0.0625,
"1001": 0.0625,
"1010": 0.0625,
"1011": 0.0625,
"1100": 0.0625,
"1101": 0.0625,
"1110": 0.0625,
"1111": 0.0625
}
}
},
{
"file": "spec/qpt.qasm",
"qasm": "include \"stdgates.inc\";\n\ngate pre q { } // pre-rotation\ngate post q { } // post-rotation\n\nqubit q;\nbit c;\nreset q;\npre q;\nbarrier q;\nh q;\nbarrier q;\npost q;\nc = measure q;\n",
"expect": {
"distribution": {
"0": 0.5,
"1": 0.5
}
}
},
{
"file": "spec/rb.qasm",
"qasm": "// One randomized benchmarking sequence\ninclude \"stdgates.inc\";\n\nqubit[2] q;\nbit[2] c;\n\nreset q;\nh q[0];\nbarrier q;\ncz q[0], q[1];\nbarrier q;\ns q[0];\ncz q[0], q[1];\nbarrier q;\ns q[0];\nz q[0];\nh q[0];\nbarrier q;\nmeasure q -> c;\n",
"expect": {
"deterministic": "00"
}
},
{
"file": "spec/rus.qasm",
"qasm": "/*\n * Repeat-until-success circuit for Rz(theta),\n * cos(theta-pi)=3/5, from Nielsen and Chuang, Chapter 4.\n */\ninclude \"stdgates.inc\";\n\n/*\n * Applies identity if out is 01, 10, or 11 and a Z-rotation by\n * theta + pi where cos(theta)=3/5 if out is 00.\n * The 00 outcome occurs with probability 5/8.\n */\ndef segment(qubit[2] anc, qubit psi) -> bit[2] {\n bit[2] b;\n reset anc;\n h anc;\n ccx anc[0], anc[1], psi;\n s psi;\n ccx anc[0], anc[1], psi;\n z psi;\n h anc;\n measure anc -> b;\n return b;\n}\n\nqubit input_qubit;\nqubit[2] ancilla;\nbit[2] flags = \"11\";\nbit output_qubit;\n\nreset input_qubit;\nh input_qubit;\n\n// braces are optional in this case\nwhile(int[2](flags) != 0) {\n flags = segment(ancilla, input_qubit);\n}\nrz(pi - arccos(3 / 5)) input_qubit;\nh input_qubit;\noutput_qubit = measure input_qubit; // should get zero\n",
"expect": {
"refused": "Q310"
}
},
{
"file": "spec/scqec.qasm",
"qasm": "/*\n * Surface code quantum memory.\n *\n * Estimate the failure probability as a function\n * of parameters at the top of the file.\n */\ninclude \"stdgates.inc\";\n\nconst int[32] d = 3; // code distance\nconst int[32] m = 10; // number of syndrome measurement cycles\nconst int[32] shots = 1000; // number of samples\nconst int[32] n = d**2; // number of code qubits\n\nuint[32] failures; // number of observed failures\n\nextern zfirst(creg[n - 1], int[32], int[32]);\nextern send(creg[n -1 ], int[32], int[32], int[32]);\nextern zlast(creg[n], int[32], int[32]) -> bit;\n\nqubit[n] data; // code qubits\nqubit[n-1] ancilla; // syndrome qubits\n/*\n * Ancilla are addressed in a (d-1) by (d-1) square array\n * followed by 4 length (d-1)/2 arrays for the top,\n * bottom, left, and right boundaries.\n */\n\nbit[n-1] layer; // syndrome outcomes in a cycle\nbit[n] data_outcomes; // data outcomes at the end\nbit outcome; // logical outcome\n\n/* Declare a sub-circuit for Hadamard gates on ancillas\n */\ndef hadamard_layer(qubit[n-1] ancilla) {\n // Hadamards in the bulk\n for uint[32] row in [0: d-2] {\n for uint[32] col in [0: d-2] {\n bit[32] sum = bit[32](row + col);\n if(sum[0] == 1)\n h ancilla[row * (d - 1) + col];\n }\n }\n // Hadamards on the left and right boundaries\n for uint[32] i in [0: d - 2] {\n h ancilla[(d - 1)**2 + (d - 1) + i];\n }\n}\n\n/* Declare a sub-circuit for a syndrome cycle.\n */\ndef cycle(qubit[n] data, qubit[n-1] ancilla) -> bit[n-1] {\n reset ancilla;\n hadamard_layer ancilla;\n\n // First round of CNOTs in the bulk\n for uint[32] row in [0: d - 2] {\n for uint[32] col in [0:d - 2] {\n bit[32] sum = bit[32](row + col);\n if(sum[0] == 0)\n cx data[row * d + col], ancilla[row * (d - 1) + col];\n if(sum[0] == 1) {\n cx ancilla[row * (d - 1) + col], data[row * d + col];\n }\n }\n }\n // First round of CNOTs on the bottom boundary\n for uint[32] i in [0: (d - 3) / 2] {\n cx data[d * (d - 1) + 2 * i], ancilla[(d - 1) ** 2 + ( d- 1) / 2 + i];\n }\n // First round of CNOTs on the right boundary\n for uint[32] i in [0: (d - 3) / 2] {\n cx ancilla[(d - 1) ** 2 + 3 * (d - 1) / 2 + i], data[2 * d - 1 + 2 * d * i];\n }\n // Remaining rounds of CNOTs, go here ...\n\n hadamard_layer ancilla;\n return measure ancilla;\n}\n\n// Loop over shots\nfor uint[32] shot in [1: shots] {\n\n // Initialize\n reset data;\n layer = cycle(data, ancilla);\n zfirst(layer, shot, d);\n\n // m cycles of syndrome measurement\n for int[32] i in [1: m] {\n layer = cycle(data, ancilla);\n send(layer, shot, i, d);\n }\n\n // Measure\n data_outcomes = measure data;\n\n outcome = zlast(data_outcomes, shot, d);\n failures += int[1](outcome);\n}\n\n/* The ratio of \"failures\" to \"shots\" is our result.\n * The data can be logged by the external functions too.\n */\n",
"expect": {
"refused": "Q125"
}
},
{
"file": "spec/t1.qasm",
"qasm": "/* Measuring the relaxation time of a qubit\n * This example demonstrates the repeated use of fixed delays.\n*/\ninclude \"stdgates.inc\";\n\nduration stride = 1us; // time resolution of points taken\nconst int[32] points = 50; // number of points taken\nconst int[32] shots = 1000; // how many shots per point\n\nint[32] counts0;\nint[32] counts1 = 0; // surviving |1> populations of qubits\n\nextern tabulate(int[32], int[32], int[32]);\n\nbit c0;\nbit c1;\n\ndefcalgrammar \"openpulse\";\n\n// define a gate calibration for an X gate on any qubit\ndefcal x q {\n play drive(q), gaussian(100, 30, 5);\n}\n\nfor int[32] p in [0 : points-1] {\n for uint i in [1 : shots] {\n // start of a basic block\n reset $0;\n reset $1;\n // excite qubits\n x $0;\n x $1;\n // wait for a fixed time indicated by loop counter\n delay[p * stride] $0;\n // wait for a fixed time indicated by loop counters\n delay[p * durationof({x $1;})];\n // read out qubit states\n c0 = measure $0;\n c1 = measure $1;\n // increment counts memories, if a 1 is seen\n counts0 += int[1](c0);\n counts1 += int[1](c1);\n }\n // log survival probability curve\n tabulate(counts0, shots, p);\n tabulate(counts1, shots, p);\n}\n",
"expect": {
"refused": "Q405"
}
},
{
"file": "spec/teleport.qasm",
"qasm": "// quantum teleportation example\nOPENQASM 3; // Version statement is optional\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit c0;\nbit c1;\nbit c2;\n// optional post-rotation for state tomography\n// empty gate body => identity gate\ngate post q { }\nreset q;\nU(0.3, 0.2, 0.1) q[0];\nh q[1];\ncx q[1], q[2];\nbarrier q;\ncx q[0], q[1];\nh q[0];\nc0 = measure q[0];\nc1 = measure q[1];\nif(c0==1) z q[2];\nif(c1==1) { x q[2]; } // braces optional in this case\npost q[2];\nc2 = measure q[2];\n",
"expect": {
"expectations": {
"Z2": 0.955336489125606
}
}
},
{
"file": "spec/varteleport.qasm",
"qasm": "/*\n * Prepare a parameterized number of Bell pairs\n * and teleport a qubit using them.\n */\ninclude \"stdgates.inc\";\n\nconst int[32] n_pairs = 10; // number of teleportations to do\n\ndef bellprep(qubit[2] q) {\n reset q;\n h q[0];\n cx q[0], q[1];\n}\n\ndef xprepare(qubit q) {\n reset q;\n h q;\n}\n\nqubit input_qubit;\nbit output_qubit;\nqubit[2*n_pairs] q;\n\nxprepare(input_qubit);\nrz(pi / 4) input_qubit;\n\nlet io = input_qubit;\nfor uint i in [0: n_pairs - 1] {\n let bp = q[{2*i, 2*i + 1}];\n bit[2] pf;\n bellprep bp;\n cx io, bp[0];\n h io;\n pf[0] = measure io;\n pf[1] = measure bp[0];\n if (pf[0]==1) z bp[1];\n if (pf[1]==1) x bp[1];\n let io = bp[1];\n}\n\nh io;\noutput_qubit = measure io; // should get zero\n",
"expect": {
"refused": "Q101"
}
},
{
"file": "spec/vqe.qasm",
"qasm": "/*\n * Variational eigensolver example\n *\n * Goal is to estimate the energy for a fixed set of parameters.\n * The parameters are updated outside of this program and a new\n * OpenQASM circuit is generated for the next iteration.\n */\ninclude \"stdgates.inc\";\n\nconst int[32] n = 10; // number of qubits\nconst int[32] layers = 3; // number of entangler layers\nconst int[32] prec = 16; // precision of all types\nconst int[32] shots = 1000; // number of shots per Pauli observable\n\n// Parameters could be written to local variables for this\n// iteration, but we will request them using extern functions\nextern get_parameter(uint[prec]) -> angle[prec];\nextern get_npaulis() -> uint[prec];\nextern get_pauli(int[prec]) -> bit[2 * n];\n\n// The energy calculation uses floating point division,\n// so we do that calculation in an extern function\nextern update_energy(int[prec], uint[prec], float[prec]) -> float[prec];\n\ngate entangler q { for uint i in [0:n-2] { cx q[i], q[i+1]; } }\ndef xmeasure(qubit q) -> bit { h q; return measure q; }\ndef ymeasure(qubit q) -> bit { s q; h q; return measure q; }\n\n/* Pauli measurement circuit.\n * The first n-bits of spec are the X component.\n * The second n-bits of spec are the Z component.\n */\ndef pauli_measurement(bit[2*n] spec, qubit[n] q) -> bit {\n bit b = 0;\n for uint[prec] i in [0: n - 1] {\n bit temp;\n if(spec[i]==1 && spec[n+i]==0) { temp = xmeasure(q[i]); }\n if(spec[i]==0 && spec[n+i]==1) { temp = measure q[i]; }\n if(spec[i]==1 && spec[n+i]==1) { temp = ymeasure(q[i]); }\n b ^= temp;\n }\n return b;\n}\n\n// Circuit to prepare trial wave function\ndef trial_circuit(qubit[n] q) {\n for int[prec] l in [0: layers - 1] {\n for uint[prec] i in [0: n - 1] {\n angle[prec] theta;\n theta = get_parameter(l * layers + i);\n ry(theta) q[i];\n }\n if(l != layers - 1) entangler q;\n }\n}\n\n/* Apply VQE ansatz circuit and measure a Pauli operator\n * given by spec. Return the number of 1 outcomes.\n */\ndef counts_for_term(bit[2*n] spec, qubit[n] q) -> uint[prec] {\n uint[prec] counts;\n for uint i in [1: shots] {\n bit b;\n reset q;\n trial_circuit q;\n b = pauli_measurement(spec, q);\n counts += int[1](b);\n }\n return counts;\n}\n\n// Estimate the expected energy\ndef estimate_energy(qubit[n] q) -> float[prec] {\n float[prec] energy;\n uint[prec] npaulis = get_npaulis();\n for int[prec] t in [0:npaulis-1] {\n bit[2*n] spec = get_pauli(t);\n uint[prec] counts;\n counts = counts_for_term(spec, q);\n energy = update_energy(t, counts, energy);\n }\n return energy;\n}\n\nqubit[n] q;\nfloat[prec] energy;\n\nenergy = estimate_energy(q);\n",
"expect": {
"refused": "Q125"
}
},
{
"file": "lang/def.qasm",
"qasm": "OPENQASM 3.1;\ninclude \"stdgates.inc\";\n// Subroutines inlined: qubits by reference, classical arguments by value,\n// values returned.\nqubit[3] q;\ndef layer(qubit a, qubit b, float[64] t) { h a; cx a, b; rz(t) b; }\ndef half(float[64] x) -> float[64] { return x / 2; }\ndef twice(int[32] k) -> int[32] { return 2 * k; }\nlayer(q[0], q[1], half(pi / 2));\nlayer(q[1], q[2], pi / twice(4));\n",
"expect": {
"equivalent": "lang/def.ref.qasm",
"exact": "ok"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrz(pi / 4) q[1];\nh q[1];\ncx q[1], q[2];\nrz(pi / 8) q[2];\n"
},
{
"file": "lang/def_measure.qasm",
"qasm": "OPENQASM 3.1;\n// Teleportation as a subroutine with bits of its own, fed forward, and a\n// subroutine whose returned measurement lands in the caller's bit.\nqubit[3] q;\nbit[1] r;\ndef teleport(qubit src, qubit mid, qubit dst) {\n bit a;\n bit b;\n h mid;\n cx mid, dst;\n cx src, mid;\n h src;\n a = measure src;\n b = measure mid;\n if (b) x dst;\n if (a) z dst;\n}\ndef read_out(qubit s) -> bit { return measure s; }\nx q[0];\nteleport(q[0], q[1], q[2]);\nr[0] = read_out(q[2]);\n",
"expect": {
"deterministic": "1"
}
},
{
"file": "lang/inv.qasm",
"qasm": "OPENQASM 3.1;\nqubit[2] q;\ngate g(a) x, y { h x; cx x, y; rz(a) y; s x; }\nh q[0];\nt q[1];\ninv @ g(0.3) q[0], q[1];\ninv @ sx q[1];\ninv @ U(0.1, 0.2, 0.3) q[0];\ninv @ iswap q[0], q[1];\n",
"expect": {
"equivalent": "lang/inv.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[2] q;\nh q[0];\nt q[1];\n// g's gates inverted, in reverse order\nsdg q[0];\nrz(-0.3) q[1];\ncx q[0], q[1];\nh q[0];\n// sx = h s h\nh q[1];\nsdg q[1];\nh q[1];\n// U(θ, φ, λ) is rz(φ) ry(θ) rz(λ) up to a phase\nrz(-0.2) q[0];\nry(-0.1) q[0];\nrz(-0.3) q[0];\n// iswap = s, s, h, cx, cx, h\nh q[1];\ncx q[1], q[0];\ncx q[0], q[1];\nh q[0];\nsdg q[1];\nsdg q[0];\n"
},
{
"file": "lang/pow.qasm",
"qasm": "OPENQASM 3.1;\nqubit[2] q;\ngate g a, b { h a; cx a, b; t b; }\npow(3) @ g q[0], q[1];\npow(-2) @ rx(0.4) q[0];\npow(4) @ t q[1];\npow(0) @ h q[0];\n",
"expect": {
"equivalent": "lang/pow.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[2] q;\nh q[0]; cx q[0], q[1]; t q[1];\nh q[0]; cx q[0], q[1]; t q[1];\nh q[0]; cx q[0], q[1]; t q[1];\nrx(-0.8) q[0];\nz q[1];\n"
},
{
"file": "lang/root.qasm",
"qasm": "OPENQASM 3.1;\nqubit[2] q;\nh q[0];\nh q[1];\npow(0.5) @ x q[0];\npow(0.5) @ z q[1];\npow(0.5) @ s q[0];\npow(0.25) @ rz(1.2) q[1];\npow(-0.5) @ x q[1];\npow(0.5) @ cz q[0], q[1];\n",
"expect": {
"equivalent": "lang/root.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[2] q;\nh q[0];\nh q[1];\nsx q[0];\ns q[1];\nt q[0];\nrz(0.3) q[1];\nsxdg q[1];\ncp(pi / 2) q[0], q[1];\n"
},
{
"file": "lang/ctrl_any.qasm",
"qasm": "OPENQASM 3.1;\n// Controls on any gate: a rotation under two controls, and a negative control.\nqubit[3] q;\nx q[0];\nx q[1];\nctrl(2) @ ry(pi / 3) q[0], q[1], q[2];\nnegctrl @ x q[2], q[0];\n",
"expect": {
"distribution": {
"010": 0.75,
"111": 0.25
}
}
},
{
"file": "lang/ctrl_user.qasm",
"qasm": "OPENQASM 3.1;\nqubit[2] q;\ngate g a { h a; t a; }\nh q[0];\nctrl @ g q[0], q[1];\nctrl @ rz(0.7) q[1], q[0];\nctrl @ U(0.1, 0.2, 0.3) q[0], q[1];\n",
"expect": {
"equivalent": "lang/ctrl_user.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[2] q;\nh q[0];\nch q[0], q[1];\ncp(pi / 4) q[0], q[1];\ncrz(0.7) q[1], q[0];\ncu(0.1, 0.2, 0.3, 0) q[0], q[1];\n"
},
{
"file": "lang/types.qasm",
"qasm": "OPENQASM 3.1;\n// Classical types, arithmetic, casts and built-in functions, folded on reading.\nqubit q;\nconst float[64] half = 0.5;\nangle[3] quarter = pi / 2;\ncomplex[float[64]] z = 1.0 + 2.0im;\nuint[8] u = 250;\nu += 3;\nint[8] k = int[8](-3.7);\nconst bit[4] mask = \"1010\";\narray[int[32], 3] a = {1, 2, 3};\nrx(half * pi) q;\nrz(quarter) q;\nry(real(z * z)) q;\nrz(float(u) / 1000) q;\nrx(k) q;\nry(popcount(mask) + a[2]) q;\nrz(sqrt(4.0) + arctan(1.0) * 4 - pi) q;\n",
"expect": {
"equivalent": "lang/types.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit q;\nrx(pi / 2) q;\nrz(pi / 2) q;\nry(-3.0) q;\nrz(0.253) q;\nrx(-3.0) q;\nry(5.0) q;\nrz(2.0) q;\n"
},
{
"file": "lang/angle_round.qasm",
"qasm": "OPENQASM 3.1;\n// An angle[4] holds 4 bits of a turn: pi/3 is held as 3/16 of a turn.\nqubit q;\nangle[4] t = pi / 3;\nh q;\nrz(t) q;\n",
"expect": {
"warns": [
"Q121"
],
"equivalent": "lang/angle_round.ref.qasm",
"exact": "ok"
},
"reference_qasm": "OPENQASM 3.1;\nqubit q;\nh q;\nrz(3 * pi / 8) q;\n"
},
{
"file": "lang/while_static.qasm",
"qasm": "OPENQASM 3.1;\nqubit[4] q;\nint i = 0;\nwhile (i < 4) {\n h q[i];\n i += 1;\n}\nint j = 3;\nwhile (true) {\n if (j < 1) break;\n x q[j];\n j -= 2;\n}\n",
"expect": {
"equivalent": "lang/while_static.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[4] q;\nh q[0];\nh q[1];\nh q[2];\nh q[3];\nx q[3];\nx q[1];\n"
},
{
"file": "lang/while_rus.qasm",
"qasm": "OPENQASM 3.1;\n// Repeat until success on measured bits, under a declared bound.\nqubit q;\nbit c;\nc = measure q;\[email protected]_iterations 3\nwhile (c == 0) {\n x q;\n c = measure q;\n}\n",
"expect": {
"warns": [
"Q311"
],
"deterministic": "1"
}
},
{
"file": "lang/while_unbounded.qasm",
"qasm": "OPENQASM 3.1;\nqubit q;\nbit c;\nc = measure q;\nwhile (c == 0) {\n x q;\n c = measure q;\n}\n",
"expect": {
"refused": "Q310"
}
},
{
"file": "lang/switch.qasm",
"qasm": "OPENQASM 3.1;\nqubit[3] q;\nbit[3] c;\nconst int mode = 2;\nswitch (mode) {\n case 1 { x q[0]; }\n case 2, 3 { x q[1]; }\n default { h q[0]; }\n}\nc[0] = measure q[1];\nc[1] = measure q[0];\nswitch (c[0:1]) {\n case 0 { }\n case 1 { x q[2]; }\n case 2 { h q[2]; }\n default { }\n}\nc[2] = measure q[2];\n",
"expect": {
"deterministic": "101"
}
},
{
"file": "lang/break_continue.qasm",
"qasm": "OPENQASM 3.1;\nqubit[8] q;\nfor int i in [0:7] {\n if (i == 2) continue;\n if (i == 5) break;\n x q[i];\n}\n",
"expect": {
"equivalent": "lang/break_continue.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[8] q;\nx q[0];\nx q[1];\nx q[3];\nx q[4];\n"
},
{
"file": "lang/runtime_break.qasm",
"qasm": "OPENQASM 3.1;\n// `break` on a measured bit: a jump forward past the rest of the loop.\nqubit q;\nqubit[3] t;\nbit c;\nbit[3] r;\nfor int i in [0:2] {\n c = measure q;\n if (c) break;\n x t[i];\n x q;\n}\nr = measure t;\n",
"expect": {
"deterministic": "0011"
}
},
{
"file": "lang/conditions.qasm",
"qasm": "OPENQASM 3.1;\n// Comparisons, &&, || and ! on measured bits.\nqubit[3] p;\nqubit[3] t;\nbit[3] c;\nbit[3] r;\nx p[1];\nx p[2];\nc = measure p;\nif (c >= 6 && !c[0]) x t[0];\nif (c < 6 || c == 7) x t[1];\nif (c > 4 && c <= 6) x t[2];\nr = measure t;\n",
"expect": {
"deterministic": "101110"
}
},
{
"file": "lang/output.qasm",
"qasm": "OPENQASM 3.1;\nqubit[2] q;\noutput bit[2] c;\noutput int[32] n = 3;\noutput float[64] t = pi / 4;\nx q[1];\nc = measure q;\n",
"expect": {
"deterministic": "10",
"outputs": [
{
"name": "c",
"type": "bit[2]",
"bits": [
0,
1
]
},
{
"name": "n",
"type": "int[32]",
"value": 3
},
{
"name": "t",
"type": "float[64]",
"value": 0.7853981633974483
}
]
}
},
{
"file": "lang/timing.qasm",
"qasm": "OPENQASM 3.1;\n// Durations carried, and labelled as not applied by the method.\nqubit[2] q;\nstretch s;\nduration d = 100ns;\nh q[0];\ndelay[2 * d] q[0];\nbox [1us] {\n cx q[0], q[1];\n}\ndelay[s] q[1];\nbarrier q;\nx[20ns] q[1];\n",
"expect": {
"warns": [
"Q403"
],
"timing": true,
"distribution": {
"01": 0.5,
"10": 0.5
}
}
},
{
"file": "lang/calibration.qasm",
"qasm": "OPENQASM 3.1;\n// Calibrations kept verbatim for export, never run.\ndefcalgrammar \"openpulse\";\ncal {\n extern port d0;\n}\ndefcal x $0 {\n play(d0, gaussian(1.0, 160dt, 40dt));\n}\nqubit q;\nh q;\n",
"expect": {
"warns": [
"Q450",
"Q451",
"Q452"
],
"calibrations": 3,
"distribution": {
"0": 0.5,
"1": 0.5
}
}
},
{
"file": "lang/stdgates.qasm",
"qasm": "OPENQASM 3.1;\ninclude \"stdgates.inc\";\n// Every standard gate, once, after a layer that leaves no symmetry.\nqubit[3] q;\nU(0.31, 0.72, -0.4) q[0];\nU(1.13, -0.25, 0.9) q[1];\nU(2.07, 0.5, 0.33) q[2];\nCX q[0], q[1];\nCX q[1], q[2];\nU(0.6, 1.1, -0.7) q[0];\nid q[0];\nx q[0];\ny q[1];\nz q[2];\nh q[0];\ns q[1];\nsdg q[2];\nt q[0];\ntdg q[1];\nsx q[2];\nsxdg q[0];\nrx(0.41) q[1];\nry(-1.3) q[2];\nrz(0.77) q[0];\np(1.9) q[1];\nphase(0.2) q[2];\nu1(-0.6) q[0];\nu2(0.3, -1.2) q[1];\nu3(0.8, 0.1, 2.2) q[2];\nU(1.4, -0.9, 0.35) q[0];\ncx q[0], q[1];\nCX q[2], q[0];\ncy q[1], q[2];\ncz q[2], q[0];\nch q[0], q[2];\ncp(0.9) q[1], q[0];\ncphase(-1.1) q[2], q[1];\ncrx(0.6) q[0], q[1];\ncry(-0.8) q[1], q[2];\ncrz(1.7) q[2], q[0];\ncu(0.5, 0.25, -0.75, 0.4) q[0], q[2];\nswap q[1], q[2];\niswap q[0], q[1];\nrxx(0.45) q[1], q[2];\nryy(-0.65) q[0], q[2];\nrzz(1.25) q[0], q[1];\nccx q[0], q[1], q[2];\nccz q[2], q[0], q[1];\ncswap q[1], q[2], q[0];\n",
"expect": {
"equivalent": "lang/stdgates.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\n// The same gates, each written in U and CX alone.\ngate d_x a { U(pi, 0, pi) a; }\ngate d_y a { U(pi, pi / 2, pi / 2) a; }\ngate d_z a { U(0, 0, pi) a; }\ngate d_h a { U(pi / 2, 0, pi) a; }\ngate d_s a { U(0, 0, pi / 2) a; }\ngate d_sdg a { U(0, 0, -pi / 2) a; }\ngate d_t a { U(0, 0, pi / 4) a; }\ngate d_tdg a { U(0, 0, -pi / 4) a; }\ngate d_p(l) a { U(0, 0, l) a; }\ngate d_rx(t) a { U(t, -pi / 2, pi / 2) a; }\ngate d_ry(t) a { U(t, 0, 0) a; }\ngate d_rz(t) a { U(0, 0, t) a; }\ngate d_sx a { d_h a; d_s a; d_h a; }\ngate d_sxdg a { d_h a; d_sdg a; d_h a; }\ngate d_cz a, b { d_h b; CX a, b; d_h b; }\ngate d_cy a, b { d_sdg b; CX a, b; d_s b; }\ngate d_ch a, b { d_h b; d_sdg b; CX a, b; d_h b; d_t b; CX a, b; d_t b; d_h b; d_s b; d_x b; d_s a; }\ngate d_cp(l) a, b { d_p(l / 2) a; CX a, b; d_p(-l / 2) b; CX a, b; d_p(l / 2) b; }\ngate d_crz(l) a, b { d_rz(l / 2) b; CX a, b; d_rz(-l / 2) b; CX a, b; }\ngate d_crx(l) a, b { d_p(pi / 2) b; CX a, b; U(-l / 2, 0, 0) b; CX a, b; U(l / 2, -pi / 2, 0) b; }\ngate d_cry(l) a, b { d_ry(l / 2) b; CX a, b; d_ry(-l / 2) b; CX a, b; }\ngate d_cu(t, f, l, g) a, b { d_p(g) a; d_p((l + f) / 2) a; d_p((l - f) / 2) b; CX a, b; U(-t / 2, 0, -(f + l) / 2) b; CX a, b; U(t / 2, f, 0) b; }\ngate d_swap a, b { CX a, b; CX b, a; CX a, b; }\ngate d_iswap a, b { d_s a; d_s b; d_h a; CX a, b; CX b, a; d_h b; }\ngate d_rzz(t) a, b { CX a, b; d_rz(t) b; CX a, b; }\ngate d_rxx(t) a, b { d_h a; d_h b; d_rzz(t) a, b; d_h a; d_h b; }\ngate d_ryy(t) a, b { d_rx(pi / 2) a; d_rx(pi / 2) b; d_rzz(t) a, b; d_rx(-pi / 2) a; d_rx(-pi / 2) b; }\ngate d_ccx a, b, c { d_h c; CX b, c; d_tdg c; CX a, c; d_t c; CX b, c; d_tdg c; CX a, c; d_t b; d_t c; d_h c; CX a, b; d_t a; d_tdg b; CX a, b; }\ngate d_ccz a, b, c { d_h c; d_ccx a, b, c; d_h c; }\ngate d_cswap a, b, c { CX c, b; d_ccx a, b, c; CX c, b; }\nqubit[3] q;\nU(0.31, 0.72, -0.4) q[0];\nU(1.13, -0.25, 0.9) q[1];\nU(2.07, 0.5, 0.33) q[2];\nCX q[0], q[1];\nCX q[1], q[2];\nU(0.6, 1.1, -0.7) q[0];\nd_x q[0];\nd_y q[1];\nd_z q[2];\nd_h q[0];\nd_s q[1];\nd_sdg q[2];\nd_t q[0];\nd_tdg q[1];\nd_sx q[2];\nd_sxdg q[0];\nd_rx(0.41) q[1];\nd_ry(-1.3) q[2];\nd_rz(0.77) q[0];\nd_p(1.9) q[1];\nd_p(0.2) q[2];\nd_p(-0.6) q[0];\nU(pi / 2, 0.3, -1.2) q[1];\nU(0.8, 0.1, 2.2) q[2];\nU(1.4, -0.9, 0.35) q[0];\nCX q[0], q[1];\nCX q[2], q[0];\nd_cy q[1], q[2];\nd_cz q[2], q[0];\nd_ch q[0], q[2];\nd_cp(0.9) q[1], q[0];\nd_cp(-1.1) q[2], q[1];\nd_crx(0.6) q[0], q[1];\nd_cry(-0.8) q[1], q[2];\nd_crz(1.7) q[2], q[0];\nd_cu(0.5, 0.25, -0.75, 0.4) q[0], q[2];\nd_swap q[1], q[2];\nd_iswap q[0], q[1];\nd_rxx(0.45) q[1], q[2];\nd_ryy(-0.65) q[0], q[2];\nd_rzz(1.25) q[0], q[1];\nd_ccx q[0], q[1], q[2];\nd_ccz q[2], q[0], q[1];\nd_cswap q[1], q[2], q[0];\n"
},
{
"file": "lang/aliases.qasm",
"qasm": "OPENQASM 3.1;\nqubit[6] q;\nqubit r;\nlet evens = q[0:2:5];\nlet tail = q[-2:] ++ r;\nh evens;\nx tail;\ncx q[{1, 3}], q[{2, 4}];\n",
"expect": {
"equivalent": "lang/aliases.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[6] q;\nqubit r;\nh q[0];\nh q[2];\nh q[4];\nx q[4];\nx q[5];\nx r;\ncx q[1], q[2];\ncx q[3], q[4];\n"
},
{
"file": "lang/physical.qasm",
"qasm": "OPENQASM 3.1;\nh $0;\ncx $0, $1;\nrz(0.2) $2;\n",
"expect": {
"equivalent": "lang/physical.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrz(0.2) q[2];\n"
},
{
"file": "lang/gphase.qasm",
"qasm": "OPENQASM 3.1;\nqubit[2] q;\nh q;\ngphase(0.3);\nctrl @ gphase(0.7) q[0];\nnegctrl @ gphase(0.2) q[1];\n",
"expect": {
"equivalent": "lang/gphase.ref.qasm"
},
"reference_qasm": "OPENQASM 3.1;\nqubit[2] q;\nh q;\np(0.7) q[0];\nx q[1];\np(0.2) q[1];\nx q[1];\n"
},
{
"file": "lang/bits_known.qasm",
"qasm": "OPENQASM 3.1;\n// A bit register set by a constant is known until a measurement writes it.\nqubit[2] q;\nbit[2] b = \"10\";\nif (b == 2) x q[0];\nif (b[0]) x q[1];\nb = measure q;\n",
"expect": {
"deterministic": "01"
}
},
{
"file": "lang/generator.qasm",
"qasm": "OPENQASM 3.1;\ninput uint n;\nqubit[n] q;\nh q[0];\nfor uint i in [0:n-2] {\n cx q[i], q[i + 1];\n}\n",
"expect": {
"inputs": {
"n": 4
},
"distribution": {
"0000": 0.5,
"1111": 0.5
},
"exact": "ok"
}
},
{
"file": "lang/qasm2.qasm",
"qasm": "OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\ncreg c[2];\nu3(pi, 0, pi) q[0];\nu2(0, pi) q[1];\nu1(pi/2) q[1];\nCX q[0], q[1];\nmeasure q -> c;\n",
"expect": {
"distribution": {
"01": 0.5,
"11": 0.5
}
}
},
{
"file": "lang/qasm2_if.qasm",
"qasm": "OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\ncreg c[2];\nx q[0];\nmeasure q -> c;\nif(c==1) x q[1];\nmeasure q[1] -> c[1];\n",
"expect": {
"deterministic": "11"
}
},
{
"file": "lang/exported.qasm",
"qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate rzx(_gate_p_0) _gate_q_0, _gate_q_1 {\n h _gate_q_1;\n cx _gate_q_0, _gate_q_1;\n rz(_gate_p_0) _gate_q_1;\n cx _gate_q_0, _gate_q_1;\n h _gate_q_1;\n}\nbit[3] c;\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrzx(1.5707963267948966) q[1], q[2];\nrzx(-1.5707963267948966) q[1], q[2];\ncx q[1], q[2];\nc[0] = measure q[0];\nc[1] = measure q[1];\nc[2] = measure q[2];\n",
"expect": {
"distribution": {
"000": 0.5,
"111": 0.5
}
}
},
{
"file": "lang/refuse_root.qasm",
"qasm": "OPENQASM 3.1;\nqubit q;\npow(0.5) @ U(0.1, 0.2, 0.3) q;\n",
"expect": {
"refused": "Q205"
}
},
{
"file": "lang/refuse_extern.qasm",
"qasm": "OPENQASM 3.1;\nextern f(int) -> int;\nqubit q;\nrz(f(1)) q;\n",
"expect": {
"refused": "Q125"
}
},
{
"file": "lang/refuse_runtime.qasm",
"qasm": "OPENQASM 3.1;\nqubit q;\nbit c;\nc = measure q;\nint k = c;\n",
"expect": {
"refused": "Q306"
}
},
{
"file": "lang/refuse_durationof.qasm",
"qasm": "OPENQASM 3.1;\nqubit q;\nduration d = durationof({ x q; });\n",
"expect": {
"refused": "Q405"
}
},
{
"file": "lang/refuse_opaque.qasm",
"qasm": "OPENQASM 2.0;\nqreg q[1];\nopaque g a;\ng q[0];\n",
"expect": {
"refused": "Q126"
}
},
{
"file": "lang/refuse_truncating.qasm",
"qasm": "OPENQASM 3.1;\nqubit q;\nrz(1/2) q;\n",
"expect": {
"refused": "Q118"
}
},
{
"file": "lang/refuse_caret.qasm",
"qasm": "qubit q;\nrz(2 ^ 3) q;\n",
"expect": {
"refused": "Q119"
}
},
{
"file": "lang/refuse_unknown_gate.qasm",
"qasm": "OPENQASM 3.1;\nqubit q;\nfoo q;\n",
"expect": {
"refused": "Q103"
}
},
{
"file": "lang/exact_not_dyadic.qasm",
"qasm": "OPENQASM 3.1;\nqubit q;\nh q;\nrz(pi / 3) q;\n",
"expect": {
"exact": "Q500",
"distribution": {
"0": 0.5,
"1": 0.5
}
}
},
{
"file": "lang/exact_dynamic.qasm",
"qasm": "OPENQASM 3.1;\nqubit[2] q;\nbit c;\nh q[0];\nc = measure q[0];\nif (c) x q[1];\n",
"expect": {
"exact": "Q502"
}
}
]
}

View file

@ -0,0 +1,8 @@
OPENQASM 3.1;
qubit[6] q;
qubit r;
let evens = q[0:2:5];
let tail = q[-2:] ++ r;
h evens;
x tail;
cx q[{1, 3}], q[{2, 4}];

View file

@ -0,0 +1,11 @@
OPENQASM 3.1;
qubit[6] q;
qubit r;
h q[0];
h q[2];
h q[4];
x q[4];
x q[5];
x r;
cx q[1], q[2];
cx q[3], q[4];

View file

@ -0,0 +1,6 @@
OPENQASM 3.1;
// An angle[4] holds 4 bits of a turn: pi/3 is held as 3/16 of a turn.
qubit q;
angle[4] t = pi / 3;
h q;
rz(t) q;

View file

@ -0,0 +1,4 @@
OPENQASM 3.1;
qubit q;
h q;
rz(3 * pi / 8) q;

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
// A bit register set by a constant is known until a measurement writes it.
qubit[2] q;
bit[2] b = "10";
if (b == 2) x q[0];
if (b[0]) x q[1];
b = measure q;

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
qubit[8] q;
for int i in [0:7] {
if (i == 2) continue;
if (i == 5) break;
x q[i];
}

View file

@ -0,0 +1,6 @@
OPENQASM 3.1;
qubit[8] q;
x q[0];
x q[1];
x q[3];
x q[4];

View file

@ -0,0 +1,11 @@
OPENQASM 3.1;
// Calibrations kept verbatim for export, never run.
defcalgrammar "openpulse";
cal {
extern port d0;
}
defcal x $0 {
play(d0, gaussian(1.0, 160dt, 40dt));
}
qubit q;
h q;

View file

@ -0,0 +1,13 @@
OPENQASM 3.1;
// Comparisons, &&, || and ! on measured bits.
qubit[3] p;
qubit[3] t;
bit[3] c;
bit[3] r;
x p[1];
x p[2];
c = measure p;
if (c >= 6 && !c[0]) x t[0];
if (c < 6 || c == 7) x t[1];
if (c > 4 && c <= 6) x t[2];
r = measure t;

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
// Controls on any gate: a rotation under two controls, and a negative control.
qubit[3] q;
x q[0];
x q[1];
ctrl(2) @ ry(pi / 3) q[0], q[1], q[2];
negctrl @ x q[2], q[0];

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
qubit[2] q;
gate g a { h a; t a; }
h q[0];
ctrl @ g q[0], q[1];
ctrl @ rz(0.7) q[1], q[0];
ctrl @ U(0.1, 0.2, 0.3) q[0], q[1];

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
qubit[2] q;
h q[0];
ch q[0], q[1];
cp(pi / 4) q[0], q[1];
crz(0.7) q[1], q[0];
cu(0.1, 0.2, 0.3, 0) q[0], q[1];

View file

@ -0,0 +1,10 @@
OPENQASM 3.1;
include "stdgates.inc";
// Subroutines inlined: qubits by reference, classical arguments by value,
// values returned.
qubit[3] q;
def layer(qubit a, qubit b, float[64] t) { h a; cx a, b; rz(t) b; }
def half(float[64] x) -> float[64] { return x / 2; }
def twice(int[32] k) -> int[32] { return 2 * k; }
layer(q[0], q[1], half(pi / 2));
layer(q[1], q[2], pi / twice(4));

View file

@ -0,0 +1,8 @@
OPENQASM 3.1;
qubit[3] q;
h q[0];
cx q[0], q[1];
rz(pi / 4) q[1];
h q[1];
cx q[1], q[2];
rz(pi / 8) q[2];

View file

@ -0,0 +1,21 @@
OPENQASM 3.1;
// Teleportation as a subroutine with bits of its own, fed forward, and a
// subroutine whose returned measurement lands in the caller's bit.
qubit[3] q;
bit[1] r;
def teleport(qubit src, qubit mid, qubit dst) {
bit a;
bit b;
h mid;
cx mid, dst;
cx src, mid;
h src;
a = measure src;
b = measure mid;
if (b) x dst;
if (a) z dst;
}
def read_out(qubit s) -> bit { return measure s; }
x q[0];
teleport(q[0], q[1], q[2]);
r[0] = read_out(q[2]);

View file

@ -0,0 +1,6 @@
OPENQASM 3.1;
qubit[2] q;
bit c;
h q[0];
c = measure q[0];
if (c) x q[1];

View file

@ -0,0 +1,4 @@
OPENQASM 3.1;
qubit q;
h q;
rz(pi / 3) q;

View file

@ -0,0 +1,19 @@
OPENQASM 3.0;
include "stdgates.inc";
gate rzx(_gate_p_0) _gate_q_0, _gate_q_1 {
h _gate_q_1;
cx _gate_q_0, _gate_q_1;
rz(_gate_p_0) _gate_q_1;
cx _gate_q_0, _gate_q_1;
h _gate_q_1;
}
bit[3] c;
qubit[3] q;
h q[0];
cx q[0], q[1];
rzx(1.5707963267948966) q[1], q[2];
rzx(-1.5707963267948966) q[1], q[2];
cx q[1], q[2];
c[0] = measure q[0];
c[1] = measure q[1];
c[2] = measure q[2];

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
input uint n;
qubit[n] q;
h q[0];
for uint i in [0:n-2] {
cx q[i], q[i + 1];
}

View file

@ -0,0 +1,6 @@
OPENQASM 3.1;
qubit[2] q;
h q;
gphase(0.3);
ctrl @ gphase(0.7) q[0];
negctrl @ gphase(0.2) q[1];

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
qubit[2] q;
h q;
p(0.7) q[0];
x q[1];
p(0.2) q[1];
x q[1];

View file

@ -0,0 +1,9 @@
OPENQASM 3.1;
qubit[2] q;
gate g(a) x, y { h x; cx x, y; rz(a) y; s x; }
h q[0];
t q[1];
inv @ g(0.3) q[0], q[1];
inv @ sx q[1];
inv @ U(0.1, 0.2, 0.3) q[0];
inv @ iswap q[0], q[1];

View file

@ -0,0 +1,24 @@
OPENQASM 3.1;
qubit[2] q;
h q[0];
t q[1];
// g's gates inverted, in reverse order
sdg q[0];
rz(-0.3) q[1];
cx q[0], q[1];
h q[0];
// sx = h s h
h q[1];
sdg q[1];
h q[1];
// U(θ, φ, λ) is rz(φ) ry(θ) rz(λ) up to a phase
rz(-0.2) q[0];
ry(-0.1) q[0];
rz(-0.3) q[0];
// iswap = s, s, h, cx, cx, h
h q[1];
cx q[1], q[0];
cx q[0], q[1];
h q[0];
sdg q[1];
sdg q[0];

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
qubit[2] q;
output bit[2] c;
output int[32] n = 3;
output float[64] t = pi / 4;
x q[1];
c = measure q;

View file

@ -0,0 +1,4 @@
OPENQASM 3.1;
h $0;
cx $0, $1;
rz(0.2) $2;

View file

@ -0,0 +1,5 @@
OPENQASM 3.1;
qubit[3] q;
h q[0];
cx q[0], q[1];
rz(0.2) q[2];

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
qubit[2] q;
gate g a, b { h a; cx a, b; t b; }
pow(3) @ g q[0], q[1];
pow(-2) @ rx(0.4) q[0];
pow(4) @ t q[1];
pow(0) @ h q[0];

View file

@ -0,0 +1,7 @@
OPENQASM 3.1;
qubit[2] q;
h q[0]; cx q[0], q[1]; t q[1];
h q[0]; cx q[0], q[1]; t q[1];
h q[0]; cx q[0], q[1]; t q[1];
rx(-0.8) q[0];
z q[1];

View file

@ -0,0 +1,9 @@
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
u3(pi, 0, pi) q[0];
u2(0, pi) q[1];
u1(pi/2) q[1];
CX q[0], q[1];
measure q -> c;

View file

@ -0,0 +1,8 @@
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
x q[0];
measure q -> c;
if(c==1) x q[1];
measure q[1] -> c[1];

View file

@ -0,0 +1,2 @@
qubit q;
rz(2 ^ 3) q;

View file

@ -0,0 +1,3 @@
OPENQASM 3.1;
qubit q;
duration d = durationof({ x q; });

View file

@ -0,0 +1,4 @@
OPENQASM 3.1;
extern f(int) -> int;
qubit q;
rz(f(1)) q;

View file

@ -0,0 +1,4 @@
OPENQASM 2.0;
qreg q[1];
opaque g a;
g q[0];

View file

@ -0,0 +1,3 @@
OPENQASM 3.1;
qubit q;
pow(0.5) @ U(0.1, 0.2, 0.3) q;

View file

@ -0,0 +1,5 @@
OPENQASM 3.1;
qubit q;
bit c;
c = measure q;
int k = c;

View file

@ -0,0 +1,3 @@
OPENQASM 3.1;
qubit q;
rz(1/2) q;

View file

@ -0,0 +1,3 @@
OPENQASM 3.1;
qubit q;
foo q;

View file

@ -0,0 +1,10 @@
OPENQASM 3.1;
qubit[2] q;
h q[0];
h q[1];
pow(0.5) @ x q[0];
pow(0.5) @ z q[1];
pow(0.5) @ s q[0];
pow(0.25) @ rz(1.2) q[1];
pow(-0.5) @ x q[1];
pow(0.5) @ cz q[0], q[1];

View file

@ -0,0 +1,10 @@
OPENQASM 3.1;
qubit[2] q;
h q[0];
h q[1];
sx q[0];
s q[1];
t q[0];
rz(0.3) q[1];
sxdg q[1];
cp(pi / 2) q[0], q[1];

View file

@ -0,0 +1,13 @@
OPENQASM 3.1;
// `break` on a measured bit: a jump forward past the rest of the loop.
qubit q;
qubit[3] t;
bit c;
bit[3] r;
for int i in [0:2] {
c = measure q;
if (c) break;
x t[i];
x q;
}
r = measure t;

View file

@ -0,0 +1,49 @@
OPENQASM 3.1;
include "stdgates.inc";
// Every standard gate, once, after a layer that leaves no symmetry.
qubit[3] q;
U(0.31, 0.72, -0.4) q[0];
U(1.13, -0.25, 0.9) q[1];
U(2.07, 0.5, 0.33) q[2];
CX q[0], q[1];
CX q[1], q[2];
U(0.6, 1.1, -0.7) q[0];
id q[0];
x q[0];
y q[1];
z q[2];
h q[0];
s q[1];
sdg q[2];
t q[0];
tdg q[1];
sx q[2];
sxdg q[0];
rx(0.41) q[1];
ry(-1.3) q[2];
rz(0.77) q[0];
p(1.9) q[1];
phase(0.2) q[2];
u1(-0.6) q[0];
u2(0.3, -1.2) q[1];
u3(0.8, 0.1, 2.2) q[2];
U(1.4, -0.9, 0.35) q[0];
cx q[0], q[1];
CX q[2], q[0];
cy q[1], q[2];
cz q[2], q[0];
ch q[0], q[2];
cp(0.9) q[1], q[0];
cphase(-1.1) q[2], q[1];
crx(0.6) q[0], q[1];
cry(-0.8) q[1], q[2];
crz(1.7) q[2], q[0];
cu(0.5, 0.25, -0.75, 0.4) q[0], q[2];
swap q[1], q[2];
iswap q[0], q[1];
rxx(0.45) q[1], q[2];
ryy(-0.65) q[0], q[2];
rzz(1.25) q[0], q[1];
ccx q[0], q[1], q[2];
ccz q[2], q[0], q[1];
cswap q[1], q[2], q[0];

View file

@ -0,0 +1,77 @@
OPENQASM 3.1;
// The same gates, each written in U and CX alone.
gate d_x a { U(pi, 0, pi) a; }
gate d_y a { U(pi, pi / 2, pi / 2) a; }
gate d_z a { U(0, 0, pi) a; }
gate d_h a { U(pi / 2, 0, pi) a; }
gate d_s a { U(0, 0, pi / 2) a; }
gate d_sdg a { U(0, 0, -pi / 2) a; }
gate d_t a { U(0, 0, pi / 4) a; }
gate d_tdg a { U(0, 0, -pi / 4) a; }
gate d_p(l) a { U(0, 0, l) a; }
gate d_rx(t) a { U(t, -pi / 2, pi / 2) a; }
gate d_ry(t) a { U(t, 0, 0) a; }
gate d_rz(t) a { U(0, 0, t) a; }
gate d_sx a { d_h a; d_s a; d_h a; }
gate d_sxdg a { d_h a; d_sdg a; d_h a; }
gate d_cz a, b { d_h b; CX a, b; d_h b; }
gate d_cy a, b { d_sdg b; CX a, b; d_s b; }
gate d_ch a, b { d_h b; d_sdg b; CX a, b; d_h b; d_t b; CX a, b; d_t b; d_h b; d_s b; d_x b; d_s a; }
gate d_cp(l) a, b { d_p(l / 2) a; CX a, b; d_p(-l / 2) b; CX a, b; d_p(l / 2) b; }
gate d_crz(l) a, b { d_rz(l / 2) b; CX a, b; d_rz(-l / 2) b; CX a, b; }
gate d_crx(l) a, b { d_p(pi / 2) b; CX a, b; U(-l / 2, 0, 0) b; CX a, b; U(l / 2, -pi / 2, 0) b; }
gate d_cry(l) a, b { d_ry(l / 2) b; CX a, b; d_ry(-l / 2) b; CX a, b; }
gate d_cu(t, f, l, g) a, b { d_p(g) a; d_p((l + f) / 2) a; d_p((l - f) / 2) b; CX a, b; U(-t / 2, 0, -(f + l) / 2) b; CX a, b; U(t / 2, f, 0) b; }
gate d_swap a, b { CX a, b; CX b, a; CX a, b; }
gate d_iswap a, b { d_s a; d_s b; d_h a; CX a, b; CX b, a; d_h b; }
gate d_rzz(t) a, b { CX a, b; d_rz(t) b; CX a, b; }
gate d_rxx(t) a, b { d_h a; d_h b; d_rzz(t) a, b; d_h a; d_h b; }
gate d_ryy(t) a, b { d_rx(pi / 2) a; d_rx(pi / 2) b; d_rzz(t) a, b; d_rx(-pi / 2) a; d_rx(-pi / 2) b; }
gate d_ccx a, b, c { d_h c; CX b, c; d_tdg c; CX a, c; d_t c; CX b, c; d_tdg c; CX a, c; d_t b; d_t c; d_h c; CX a, b; d_t a; d_tdg b; CX a, b; }
gate d_ccz a, b, c { d_h c; d_ccx a, b, c; d_h c; }
gate d_cswap a, b, c { CX c, b; d_ccx a, b, c; CX c, b; }
qubit[3] q;
U(0.31, 0.72, -0.4) q[0];
U(1.13, -0.25, 0.9) q[1];
U(2.07, 0.5, 0.33) q[2];
CX q[0], q[1];
CX q[1], q[2];
U(0.6, 1.1, -0.7) q[0];
d_x q[0];
d_y q[1];
d_z q[2];
d_h q[0];
d_s q[1];
d_sdg q[2];
d_t q[0];
d_tdg q[1];
d_sx q[2];
d_sxdg q[0];
d_rx(0.41) q[1];
d_ry(-1.3) q[2];
d_rz(0.77) q[0];
d_p(1.9) q[1];
d_p(0.2) q[2];
d_p(-0.6) q[0];
U(pi / 2, 0.3, -1.2) q[1];
U(0.8, 0.1, 2.2) q[2];
U(1.4, -0.9, 0.35) q[0];
CX q[0], q[1];
CX q[2], q[0];
d_cy q[1], q[2];
d_cz q[2], q[0];
d_ch q[0], q[2];
d_cp(0.9) q[1], q[0];
d_cp(-1.1) q[2], q[1];
d_crx(0.6) q[0], q[1];
d_cry(-0.8) q[1], q[2];
d_crz(1.7) q[2], q[0];
d_cu(0.5, 0.25, -0.75, 0.4) q[0], q[2];
d_swap q[1], q[2];
d_iswap q[0], q[1];
d_rxx(0.45) q[1], q[2];
d_ryy(-0.65) q[0], q[2];
d_rzz(1.25) q[0], q[1];
d_ccx q[0], q[1], q[2];
d_ccz q[2], q[0], q[1];
d_cswap q[1], q[2], q[0];

View file

@ -0,0 +1,18 @@
OPENQASM 3.1;
qubit[3] q;
bit[3] c;
const int mode = 2;
switch (mode) {
case 1 { x q[0]; }
case 2, 3 { x q[1]; }
default { h q[0]; }
}
c[0] = measure q[1];
c[1] = measure q[0];
switch (c[0:1]) {
case 0 { }
case 1 { x q[2]; }
case 2 { h q[2]; }
default { }
}
c[2] = measure q[2];

View file

@ -0,0 +1,13 @@
OPENQASM 3.1;
// Durations carried, and labelled as not applied by the method.
qubit[2] q;
stretch s;
duration d = 100ns;
h q[0];
delay[2 * d] q[0];
box [1us] {
cx q[0], q[1];
}
delay[s] q[1];
barrier q;
x[20ns] q[1];

View file

@ -0,0 +1,18 @@
OPENQASM 3.1;
// Classical types, arithmetic, casts and built-in functions, folded on reading.
qubit q;
const float[64] half = 0.5;
angle[3] quarter = pi / 2;
complex[float[64]] z = 1.0 + 2.0im;
uint[8] u = 250;
u += 3;
int[8] k = int[8](-3.7);
const bit[4] mask = "1010";
array[int[32], 3] a = {1, 2, 3};
rx(half * pi) q;
rz(quarter) q;
ry(real(z * z)) q;
rz(float(u) / 1000) q;
rx(k) q;
ry(popcount(mask) + a[2]) q;
rz(sqrt(4.0) + arctan(1.0) * 4 - pi) q;

View file

@ -0,0 +1,9 @@
OPENQASM 3.1;
qubit q;
rx(pi / 2) q;
rz(pi / 2) q;
ry(-3.0) q;
rz(0.253) q;
rx(-3.0) q;
ry(5.0) q;
rz(2.0) q;

View file

@ -0,0 +1,10 @@
OPENQASM 3.1;
// Repeat until success on measured bits, under a declared bound.
qubit q;
bit c;
c = measure q;
@wai.max_iterations 3
while (c == 0) {
x q;
c = measure q;
}

View file

@ -0,0 +1,13 @@
OPENQASM 3.1;
qubit[4] q;
int i = 0;
while (i < 4) {
h q[i];
i += 1;
}
int j = 3;
while (true) {
if (j < 1) break;
x q[j];
j -= 2;
}

View file

@ -0,0 +1,8 @@
OPENQASM 3.1;
qubit[4] q;
h q[0];
h q[1];
h q[2];
h q[3];
x q[3];
x q[1];

View file

@ -0,0 +1,8 @@
OPENQASM 3.1;
qubit q;
bit c;
c = measure q;
while (c == 0) {
x q;
c = measure q;
}

View file

@ -0,0 +1,690 @@
{
"format": "wai.qasm3-conformance/1",
"about": "OpenQASM 3 programs, each with what the reader must do with it. spec/ holds the language specification's example programs, unchanged; lang/ holds a program for each construct, and references written without it.",
"expectations": {
"refused": "reading refuses the program with this diagnostic code",
"warns": "reading succeeds, and these are the codes of every warning and note it gives, sorted",
"deterministic": "every shot of a run gives this outcome (bit n-1 first)",
"distribution": "the exact probabilities of the outcomes of a program whose measurements all come at the end, within 1e-9",
"expectations": "Pauli expectation values of the state before the final measurements, exact, within 1e-9",
"equivalent": "the program's unitary equals the named reference's, up to a global phase, within 1e-9",
"exact": "the bit-exact circuit takes the program (ok), or refuses it with this code",
"outputs": "the run's result lists these outputs",
"timing": "the run's result labels its durations as not applied",
"calibrations": "the program keeps this many calibration statements verbatim",
"inputs": "the generator's inputs"
},
"cases": [
{
"file": "spec/adder.qasm",
"covers": [
"gate definitions",
"uint indexed as bits",
"casts",
"for over a range with a negative step",
"slices in measurement"
],
"expect": {
"deterministic": "10000"
}
},
{
"file": "spec/alignment.qasm",
"covers": [
"stretch",
"delay with a stretch expression",
"barrier"
],
"expect": {
"warns": [
"Q403"
],
"timing": true,
"distribution": {
"000": 0.8535533905932737,
"100": 0.14644660940672624
}
}
},
{
"file": "spec/arrays.qasm",
"covers": [
"arrays",
"nested array values",
"slices",
"sizeof",
"an index past the array's end (the example's own: `a - 1` is 4)"
],
"expect": {
"refused": "Q107"
}
},
{
"file": "spec/cphase.qasm",
"covers": [
"a fragment: no qubits declared"
],
"expect": {
"refused": "Q102"
}
},
{
"file": "spec/dd.qasm",
"covers": [
"durationof"
],
"expect": {
"refused": "Q405"
}
},
{
"file": "spec/defcal.qasm",
"covers": [
"defcal",
"defcalgrammar"
],
"expect": {
"refused": "Q100"
}
},
{
"file": "spec/gateteleport.qasm",
"covers": [
"extern",
"subroutine bits"
],
"expect": {
"refused": "Q125"
}
},
{
"file": "spec/inverseqft1.qasm",
"covers": [
"conditions on a cast of measured bits"
],
"expect": {
"deterministic": "0000"
}
},
{
"file": "spec/inverseqft2.qasm",
"covers": [
"conditions on single bits",
"measure ->"
],
"expect": {
"deterministic": "0000"
}
},
{
"file": "spec/ipe.qasm",
"covers": [
"angle arithmetic on measured bits"
],
"expect": {
"refused": "Q128"
}
},
{
"file": "spec/msd.qasm",
"covers": [
"subroutines",
"an index outside its register"
],
"expect": {
"refused": "Q107"
}
},
{
"file": "spec/qec.qasm",
"covers": [
"a subroutine returning measured bits",
"conditions on a cast"
],
"expect": {
"deterministic": "01000"
}
},
{
"file": "spec/qft.qasm",
"covers": [
"cphase",
"reset before the first gate"
],
"expect": {
"distribution": {
"0000": 0.0625,
"0001": 0.0625,
"0010": 0.0625,
"0011": 0.0625,
"0100": 0.0625,
"0101": 0.0625,
"0110": 0.0625,
"0111": 0.0625,
"1000": 0.0625,
"1001": 0.0625,
"1010": 0.0625,
"1011": 0.0625,
"1100": 0.0625,
"1101": 0.0625,
"1110": 0.0625,
"1111": 0.0625
}
}
},
{
"file": "spec/qpt.qasm",
"covers": [
"empty gate bodies",
"barrier"
],
"expect": {
"distribution": {
"0": 0.5,
"1": 0.5
}
}
},
{
"file": "spec/rb.qasm",
"covers": [
"a Clifford sequence"
],
"expect": {
"deterministic": "00"
}
},
{
"file": "spec/rus.qasm",
"covers": [
"while on measured bits with no bound",
"bits set by a constant"
],
"expect": {
"refused": "Q310"
}
},
{
"file": "spec/scqec.qasm",
"covers": [
"subroutines called as gates",
"bits known on reading",
"extern"
],
"expect": {
"refused": "Q125"
}
},
{
"file": "spec/t1.qasm",
"covers": [
"durations",
"durationof"
],
"expect": {
"refused": "Q405"
}
},
{
"file": "spec/teleport.qasm",
"covers": [
"teleportation",
"conditions",
"an empty gate"
],
"expect": {
"expectations": {
"Z2": 0.955336489125606
}
}
},
{
"file": "spec/varteleport.qasm",
"covers": [
"let rebinding a name of an enclosing scope"
],
"expect": {
"refused": "Q101"
}
},
{
"file": "spec/vqe.qasm",
"covers": [
"extern"
],
"expect": {
"refused": "Q125"
}
},
{
"file": "lang/def.qasm",
"covers": [
"def",
"return"
],
"expect": {
"equivalent": "lang/def.ref.qasm",
"exact": "ok"
}
},
{
"file": "lang/def_measure.qasm",
"covers": [
"def with bits of its own",
"a returned measurement"
],
"expect": {
"deterministic": "1"
}
},
{
"file": "lang/inv.qasm",
"covers": [
"inv @ on standard and user gates"
],
"expect": {
"equivalent": "lang/inv.ref.qasm"
}
},
{
"file": "lang/pow.qasm",
"covers": [
"pow(k) @, k a whole number"
],
"expect": {
"equivalent": "lang/pow.ref.qasm"
}
},
{
"file": "lang/root.qasm",
"covers": [
"pow(r) @, r fractional, exact roots"
],
"expect": {
"equivalent": "lang/root.ref.qasm"
}
},
{
"file": "lang/ctrl_any.qasm",
"covers": [
"ctrl @ on a rotation",
"negctrl @"
],
"expect": {
"distribution": {
"010": 0.75,
"111": 0.25
}
}
},
{
"file": "lang/ctrl_user.qasm",
"covers": [
"ctrl @ on a user gate, rz and U"
],
"expect": {
"equivalent": "lang/ctrl_user.ref.qasm"
}
},
{
"file": "lang/types.qasm",
"covers": [
"float",
"angle",
"complex",
"uint",
"bit arrays",
"arrays",
"casts",
"built-in functions"
],
"expect": {
"equivalent": "lang/types.ref.qasm"
}
},
{
"file": "lang/angle_round.qasm",
"covers": [
"angle[n] rounding"
],
"expect": {
"warns": [
"Q121"
],
"equivalent": "lang/angle_round.ref.qasm",
"exact": "ok"
}
},
{
"file": "lang/while_static.qasm",
"covers": [
"while known on reading",
"break"
],
"expect": {
"equivalent": "lang/while_static.ref.qasm"
}
},
{
"file": "lang/while_rus.qasm",
"covers": [
"while on measured bits under a declared bound"
],
"expect": {
"warns": [
"Q311"
],
"deterministic": "1"
}
},
{
"file": "lang/while_unbounded.qasm",
"covers": [
"while on measured bits"
],
"expect": {
"refused": "Q310"
}
},
{
"file": "lang/switch.qasm",
"covers": [
"switch known on reading",
"switch on measured bits"
],
"expect": {
"deterministic": "101"
}
},
{
"file": "lang/break_continue.qasm",
"covers": [
"break",
"continue"
],
"expect": {
"equivalent": "lang/break_continue.ref.qasm"
}
},
{
"file": "lang/runtime_break.qasm",
"covers": [
"break on a measured bit"
],
"expect": {
"deterministic": "0011"
}
},
{
"file": "lang/conditions.qasm",
"covers": [
"comparisons, &&, || and ! on measured bits"
],
"expect": {
"deterministic": "101110"
}
},
{
"file": "lang/output.qasm",
"covers": [
"output"
],
"expect": {
"deterministic": "10",
"outputs": [
{
"name": "c",
"type": "bit[2]",
"bits": [
0,
1
]
},
{
"name": "n",
"type": "int[32]",
"value": 3
},
{
"name": "t",
"type": "float[64]",
"value": 0.7853981633974483
}
]
}
},
{
"file": "lang/timing.qasm",
"covers": [
"delay",
"box",
"stretch",
"barrier",
"gate durations"
],
"expect": {
"warns": [
"Q403"
],
"timing": true,
"distribution": {
"01": 0.5,
"10": 0.5
}
}
},
{
"file": "lang/calibration.qasm",
"covers": [
"defcal",
"cal",
"defcalgrammar"
],
"expect": {
"warns": [
"Q450",
"Q451",
"Q452"
],
"calibrations": 3,
"distribution": {
"0": 0.5,
"1": 0.5
}
}
},
{
"file": "lang/stdgates.qasm",
"covers": [
"every standard gate, U and CX"
],
"expect": {
"equivalent": "lang/stdgates.ref.qasm"
}
},
{
"file": "lang/aliases.qasm",
"covers": [
"let",
"slices",
"index sets",
"negative indices",
"++"
],
"expect": {
"equivalent": "lang/aliases.ref.qasm"
}
},
{
"file": "lang/physical.qasm",
"covers": [
"physical qubits"
],
"expect": {
"equivalent": "lang/physical.ref.qasm"
}
},
{
"file": "lang/gphase.qasm",
"covers": [
"gphase",
"ctrl @ gphase"
],
"expect": {
"equivalent": "lang/gphase.ref.qasm"
}
},
{
"file": "lang/bits_known.qasm",
"covers": [
"bits set by a constant"
],
"expect": {
"deterministic": "01"
}
},
{
"file": "lang/generator.qasm",
"covers": [
"input"
],
"expect": {
"inputs": {
"n": 4
},
"distribution": {
"0000": 0.5,
"1111": 0.5
},
"exact": "ok"
}
},
{
"file": "lang/qasm2.qasm",
"covers": [
"OpenQASM 2: qreg, creg, u1, u2, u3, CX, measure ->"
],
"expect": {
"distribution": {
"01": 0.5,
"11": 0.5
}
}
},
{
"file": "lang/qasm2_if.qasm",
"covers": [
"OpenQASM 2: if(c==n)"
],
"expect": {
"deterministic": "11"
}
},
{
"file": "lang/exported.qasm",
"covers": [
"a program as converters write one: gates with generated names, decimal angles"
],
"expect": {
"distribution": {
"000": 0.5,
"111": 0.5
}
}
},
{
"file": "lang/refuse_root.qasm",
"covers": [
"a fractional power with no exact gate"
],
"expect": {
"refused": "Q205"
}
},
{
"file": "lang/refuse_extern.qasm",
"covers": [
"extern"
],
"expect": {
"refused": "Q125"
}
},
{
"file": "lang/refuse_runtime.qasm",
"covers": [
"classical arithmetic on measured bits"
],
"expect": {
"refused": "Q306"
}
},
{
"file": "lang/refuse_durationof.qasm",
"covers": [
"durationof"
],
"expect": {
"refused": "Q405"
}
},
{
"file": "lang/refuse_opaque.qasm",
"covers": [
"opaque"
],
"expect": {
"refused": "Q126"
}
},
{
"file": "lang/refuse_truncating.qasm",
"covers": [
"a truncating division as a real"
],
"expect": {
"refused": "Q118"
}
},
{
"file": "lang/refuse_caret.qasm",
"covers": [
"^ between integers with no version"
],
"expect": {
"refused": "Q119"
}
},
{
"file": "lang/refuse_unknown_gate.qasm",
"covers": [
"an unknown gate"
],
"expect": {
"refused": "Q103"
}
},
{
"file": "lang/exact_not_dyadic.qasm",
"covers": [
"the bit-exact method: an angle that is not dyadic"
],
"expect": {
"exact": "Q500",
"distribution": {
"0": 0.5,
"1": 0.5
}
}
},
{
"file": "lang/exact_dynamic.qasm",
"covers": [
"the bit-exact method: a dynamic program"
],
"expect": {
"exact": "Q502"
}
}
]
}

View file

@ -0,0 +1,44 @@
/*
* quantum ripple-carry adder
* Cuccaro et al, quant-ph/0410184
*/
include "stdgates.inc";
gate majority a, b, c {
cx c, b;
cx c, a;
ccx a, b, c;
}
gate unmaj a, b, c {
ccx a, b, c;
cx c, a;
cx a, b;
}
qubit[1] cin;
qubit[4] a;
qubit[4] b;
qubit[1] cout;
bit[5] ans;
uint[4] a_in = 1; // a = 0001
uint[4] b_in = 15; // b = 1111
// initialize qubits
reset cin;
reset a;
reset b;
reset cout;
// set input states
for uint i in [0: 3] {
if(bool(a_in[i])) x a[i];
if(bool(b_in[i])) x b[i];
}
// add a to b, storing result in b
majority cin[0], b[0], a[0];
for uint i in [0: 2] { majority a[i], b[i + 1], a[i + 1]; }
cx a[3], cout[0];
for uint i in [2: -1: 0] { unmaj a[i],b[i+1],a[i+1]; }
unmaj cin[0], b[0], a[0];
measure b[0:3] -> ans[0:3];
measure cout[0] -> ans[4];

View file

@ -0,0 +1,16 @@
/* CPMG XY4 decoupling
* This example demonstrates the use of stretch to
* specify design intent on gate alignment, without
* being tied to physical qubits and gates.
*/
include "stdgates.inc";
stretch g;
qubit[3] q;
barrier q;
cx q[0], q[1];
delay[g] q[2];
U(pi/4, 0, pi/2) q[2];
delay[2*g] q[2];
barrier q;

View file

@ -0,0 +1,101 @@
OPENQASM 3.0; // Version statement is optional
// ==================
// Array declarations
// ==================
// Declare 'my_ints' to be a 1D array of 8-bit signed integers, where the index
// runs from 0 to 15.
array[int[8], 16] my_ints;
// Declare 'my_doubles' to be a 2D array of double-precision floating-point
// values, where the first index runs from 0 to 7 and the second from 0 to 3.
array[float[64], 8, 4] my_doubles;
// Initialise a 1D array with fixed values.
array[uint[32], 4] my_defined_uints = {5, 6, 7, 8};
// Initialise a 2D array with fixed values.
array[float[32], 4, 2] my_defined_floats = {
{0.5, 0.5},
{1.0, 2.0},
{-0.4, 0.7},
{1.3, -2.1e-2}
};
// Initialise an array using a row of another array. Note that this copies the
// data; changes to the values in `my_defined_float_row` will not affect
// `my_defined_floats`.
array[float[32], 2] my_defined_float_row = my_defined_floats[0];
// Arrays can be defined in terms of compile-time constant dimension sizes, and
// can use expressions in the slots of their initialisers.
const uint[8] DIM_SIZE = 2;
array[int[8], DIM_SIZE, DIM_SIZE] all_ones = {{2+3, 4-1}, {3+8, 12-4}};
// =============================
// Array indexing and operations
// =============================
// Use `[]` notation to access array elements. The dimensions are
// comma-separated.
uint[8] a = my_defined_uints[0];
float[32] b = my_defined_floats[2, 1];
// The same notation is used in simple assignments.
my_defined_uints[1] = 5;
my_defined_floats[3, 0] = -0.45;
// In both, the indices can be (non-constant) expressions.
my_defined_uints[a - 1] = a + 1;
// Assignments can also be done using an array, or array slice, on the left-hand
// side, provided the right-hand side evaluates to an array of exactly the same
// shape. The assignment is done by copy, so subsequent changes to either of
// the two parts will not affect the other.
my_defined_floats[2] = my_defined_float_row;
// You can also use the slice notation to set elements. Beware that it is
// generally a logical error to read and write from overlapping slices at the
// same time, and that OpenQASM 3 makes no guarantees about which order the data
// will be read from and written to. You might clobber data if you overlap.
my_defined_floats[0:1] = my_defined_floats[2:3];
// The ``sizeof`` operator returns the size of an array's dimension.
const uint[32] dimension = sizeof(my_defined_uints); // assigns 4.
// The ``sizeof`` operator can also be used in two-argument form, where the
// second argument is the index of the dimension, counting from 0.
const uint[32] first_dimension = sizeof(my_doubles, 0); // returns 8
const uint[32] second_dimension = sizeof(my_doubles, 1); // returns 4
// If the second argument is omitted from ``sizeof`` and the given array is
// multi-dimensional, it defaults to returning the first dimension, so
// ``sizeof(my_array) == sizeof(my_array, 0)`` in all circumstances.
const uint[32] first_dimension = sizeof(my_doubles); // still 8.
// =====================
// Arrays in subroutines
// =====================
// Array arguments have a mandatory ``readonly`` or ``mutable`` specifier when
// defined in a subroutine argument list. This is because arrays are passed to
// subroutines by reference, not by value, so modifications will propagate back
// to the original data. Such modifications are only allowed for ``mutable``
// references, not ``readonly``.
def copy_3_bytes(readonly array[uint[8], 3] in_array, mutable array[uint[8], 3] out_array) {
// Within this block, ``in_array`` can be read from, but not written to,
// whereas ``out_array`` can be both read from and written to.
}
// When specifying array subroutine parameters, there is a second format where
// the sizes of the dimensions are not given explicitly, only the number of
// dimensions. This is where the ``sizeof`` operator is most useful. In these
// cases, `sizeof` is _not_ a compile-time constant.
def multi_dimensional_input(readonly array[int[32], #dim=3] my_array) {
uint[32] dimension_0 = sizeof(my_array, 0);
uint[32] dimension_1 = sizeof(my_array, 1);
uint[32] dimension_2 = sizeof(my_array, 2);
}

View file

@ -0,0 +1,9 @@
gate cphase(θ) a, b
{
U(0, 0, θ / 2) a;
CX a, b;
U(0, 0, -θ / 2) b;
CX a, b;
U(0, 0, θ / 2) b;
}
cphase(π / 2) q[0], q[1];

View file

@ -0,0 +1,26 @@
/* CPMG XY4 decoupling w/ boxing
* This example demonstrates the use of referential delays
* and time alignment.
*/
include "stdgates.inc";
stretch a;
duration start_stretch = -0.5 * durationof({x $0;}) + a;
duration middle_stretch = -0.5 * durationof({x $0;}) - 5 * durationof({y $0;}) + a;
duration end_stretch = -0.5 * durationof({y $0;}) + a;
box {
delay[start_stretch] $0;
x $0;
delay[middle_stretch] $0;
y $0;
delay[middle_stretch] $0;
x $0;
delay[middle_stretch] $0;
y $0;
delay[end_stretch] $0;
cx $2, $3;
cx $1, $2;
u $3;
}

View file

@ -0,0 +1,36 @@
defcalgrammar "openpulse";
defcal x $0 {
play drive($0), gaussian(...);
}
defcal x $1 {
play drive($1), gaussian(...);
}
defcal rz(angle[20] theta) q {
shift_phase drive(q), -theta;
}
defcal measure $0 -> bit {
complex[int[24]] iq;
bit state;
complex[int[12]] k0[1024] = [i0 + q0*j, i1 + q1*j, i2 + q2*j, ...];
play measure($0), flat_top_gaussian(...);
iq = capture acquire($0), 2048, extern(k0);
return threshold(iq, 1234);
}
defcal zx90_ix $0, $1 {
play drive($0, "cr1"), flat_top_gaussian(...); // uses a non-default
// frame labeled "cr1"
}
defcal cx $0, $1 {
zx90_ix $0, $1;
x $0;
shift_phase drive($0, "cr1");
zx90_ix $0, $1;
x $0;
x $1;
}

View file

@ -0,0 +1,30 @@
// Repetition code gate teleportation
include "stdgates.inc";
// declarations
const int[32] n = 3;
extern vote(bit[n]) -> bit;
def logical_meas(qubit[3] d) -> bit {
bit[3] c;
bit r;
measure d -> c;
r = vote(c);
return r;
}
qubit[3] q;
qubit[3] a;
bit r;
// prep magic state
rz(pi/4) a;
// entangle two logical registers
cx q, a;
// measure out the ancilla
r = logical_meas(a);
// if we get a logical |1> then we need to apply a logical Z correction
if (r == 1) z q;

View file

@ -0,0 +1,27 @@
// QFT and measure, version 1
include "stdgates.inc";
qubit[4] q;
bit[4] c;
reset q;
h q;
barrier q;
h q[0];
c[0] = measure q[0];
if(int[4](c) == 1) { rz(pi / 2) q[1]; }
h q[1];
c[1] = measure q[1];
if(int[4](c) == 1){ rz(pi / 4) q[2]; }
if(int[4](c) == 2){ rz(pi / 2) q[2]; }
if(int[4](c) == 3){ rz(pi / 2 + pi / 4) q[2]; }
h q[2];
c[2] = measure q[2];
if(int[4](c) == 1) rz(pi / 8) q[3];
if(int[4](c) == 2) rz(pi / 4) q[3];
if(int[4](c) == 3) rz(pi/4+pi/8) q[3];
if(int[4](c) == 4) rz(pi / 2) q[3];
if(int[4](c) == 5) rz(pi / 2 + pi / 8) q[3];
if(int[4](c) == 6) rz(pi / 2+ pi / 4) q[3];
if(int[4](c) == 7) rz(pi / 2 + pi / 4 + pi / 8) q[3];
h q[3];
c[3] = measure q[3];

View file

@ -0,0 +1,26 @@
// QFT and measure, version 2
include "stdgates.inc";
qubit[4] q;
bit c0;
bit c1;
bit c2;
bit c3;
reset q;
h q;
barrier q;
h q[0];
measure q[0] -> c0;
if(c0 == 1) { rz(pi / 2) q[1]; }
h q[1];
measure q[1] -> c1;
if(c0==1) { rz(pi / 4) q[2]; }
if(c1==1) { rz(pi / 2) q[2]; }
h q[2];
measure q[2] -> c2;
if(c0 == 1) { rz(pi / 8) q[3]; }
if(c1 == 1) { rz(pi / 4) q[3]; }
if(c2 == 1) { rz(pi / 2) q[3]; }
h q[3];
measure q[3] -> c3;

View file

@ -0,0 +1,37 @@
/*
* Iterative phase estimation
*/
include "stdgates.inc";
const int[32] n = 10; // number of iterations
const float[32] theta = 3 * pi / 8; // phase angle on target qubit
qubit q; // phase estimation qubit
qubit r; // target qubit for the controlled-unitary gate
angle[n] c; // phase estimation bits
// initialize
reset q;
reset r;
// prepare uniform superposition of eigenvectors of phase
h r;
// iterative phase estimation loop
uint[n] power = 1;
for uint i in [0: n - 1] { // implicitly cast val to int
reset q;
h q;
ctrl @ pow(power) @ phase(theta) q, r;
inv @ phase(c) q;
h q;
measure q -> c[0];
// newest measurement outcome is associated to a pi/2 phase shift
// in the next iteration, so shift all bits of c left
c <<= 1;
power <<= 1;
}
// Now c contains the n-bit estimate of phi in the
// eigenvalue e^{i*phi} and qreg r is projected to an
// approximate eigenstate of the phase gate.

View file

@ -0,0 +1,171 @@
/*
* Magic state distillation and computation
*/
include "stdgates.inc";
const int[32] buffer_size = 6; // size of magic state buffer
// Y-basis measurement
def ymeasure(qubit q) -> bit {
s q;
h q;
return measure q;
}
/*
* Distillation subroutine takes 10 |H> magic states
* and 3 scratch qubits that will be reinitialized.
* The first two input magic states are the outputs.
* The subroutine returns a success bit that is true
* on success and false otherwise (see arXiv:1811.00566).
*/
def distill(qubit[10] magic, qubit[3] scratch) -> bool {
bit temp;
bit[3] checks;
// Encode two magic states in the [[4,2,2]] code
reset scratch[0: 1];
h scratch[1];
cx scratch[1], magic[0];
cx magic[1], scratch[0];
cx magic[0], scratch[0];
cx scratch[1], magic[1];
// Body of distillation circuit
cy magic[2], scratch[0];
h magic[1];
temp = ymeasure(magic[2]);
if(temp == 1) { ry(-pi / 2) scratch[0]; }
reset scratch[2];
h scratch[2];
cz scratch[2], scratch[0];
cy magic[3], scratch[0];
temp = ymeasure(magic[3]);
if(temp==0) { ry(pi / 2) scratch[0]; }
h scratch[0];
s scratch[0];
cy magic[4], scratch[1];
temp = ymeasure(magic[4]);
if(temp==1) { ry(-pi / 2) scratch[1]; }
cz scratch[3], scratch[2];
cy magic[5], scratch[1];
temp = ymeasure(magic[5]);
if(temp==0) { ry(pi / 2) scratch[1]; }
cy scratch[0], magic[1];
inv @ s scratch[1];
cz scratch[0], scratch[1];
h scratch[0];
cy scratch[1], magic[1];
cy magic[6], scratch[0];
temp = ymeasure(magic[6]);
if(temp == 1) { ry(-pi / 2) scratch[0]; }
cz scratch[2], scratch[1];
cz scratch[2], scratch[0];
cy magic[7], scratch[0];
temp = ymeasure(magic[7]);
if(temp == 0) ry(pi / 2) scratch[0];
cy magic[8], scratch[1];
temp = ymeasure(magic[8]);
if(temp==1) { ry(-pi / 2) scratch[1]; }
cz scratch[2], scratch[1];
cy magic[9], scratch[1];
temp = ymeasure(magic[9]);
if(temp == 0) { ry(pi / 2) scratch[1]; }
h scratch[2];
// Decode [[4,2,2]] code
cx magic[0], scratch[0];
cx scratch[1], magic[1];
cx magic[1], scratch[0];
cx scratch[1], magic[0];
h scratch[1];
checks = measure scratch;
success = ~(bool(checks[0]) | bool(checks[1]) | bool(checks[2]));
return success;
}
// Repeat level-0 distillation until success
def rus_level_0(qubit[10] magic, qubit[3] scratch) {
bool success;
while(~success) {
reset magic;
ry(pi / 4) magic;
success = distill(magic, scratch);
}
}
/*
* Run two levels of 10:2 magic state distillation.
* Both levels have two distillations running in parallel.
* The output pairs from the first level are separated and
* input to different second levels distillation circuits
* because a failure in a first level circuit can lead to
* errors on both outputs.
* Put the requested even number of copies into the buffer.
*/
def distill_and_buffer(int[32] num, qubit[33] work, qubit[buffer_size] buffer) {
int[32] index;
bit success_0;
bit success_1;
let magic_lvl0 = work[0: 9];
let magic_lvl1_0 = work[10: 19];
let magic_lvl1_1 = work[20: 29];
let scratch = work[30: 32];
// Run first-level circuits until 10 successes,
// storing the outputs for use in the second level
for uint i in [0: 9] {
rus_level_0 magic_lvl0, scratch;
swap magic_lvl0[0], magic_lvl1_0[i];
swap magic_lvl0[1], magic_lvl1_1[i];
}
// Run two second level circuits simultaneously
success_0 = distill(magic_lvl1_0, scratch);
success_1 = distill(magic_lvl1_1, scratch);
// Move usable magic states into the buffer register
if(success_0 && index < buffer_size) {
swap magic_lvl1_0[0: 1], buffer[index: index + 1];
index += 2;
}
if(success_1 && index < buffer_size) {
swap magic_lvl1_1[0: 1], buffer[index: index + 1];
index += 2;
}
}
// Apply Ry(pi/4) to a qubit by consuming a magic state
// from the magic state buffer at address "addr"
def Ty(int[32] addr, qubit q, qubit[buffer_size] buffer) {
bit outcome;
cy buffer[addr], q;
outcome = ymeasure(buffer[addr]);
if(outcome == 1) ry(pi / 2) q;
}
qubit[33] workspace;
qubit[buffer_size] buffer;
qubit[5] q;
bit[5] c;
int[32] address;
// initialize
reset workspace;
reset buffer;
reset q;
distill_and_buffer(buffer_size) workspace, buffer;
// Consume magic states to apply some gates ...
h q[0];
cx q[0], q[1];
Ty(address) q[0], buffer;
address += 1;
cx q[0], q[1];
Ty(address) q[1], buffer;
address += 1;
// In principle each Ty gate can execute as soon as the magic
// state is available at the address in the buffer register.
// We can continue alternating state distillation and computation
// to refill and empty a circular buffer.

View file

@ -0,0 +1,26 @@
// Repetition code syndrome measurement
include "stdgates.inc";
qubit[3] q;
qubit[2] a;
bit[3] c;
bit[2] syn;
def syndrome(qubit[3] d, qubit[2] a) -> bit[2] {
bit[2] b;
cx d[0], a[0];
cx d[1], a[0];
cx d[1], a[1];
cx d[2], a[1];
measure a -> b;
return b;
}
reset q;
reset a;
x q[0]; // insert an error
barrier q;
syn = syndrome(q, a);
if(int[2](syn)==1) x q[0];
if(int[2](syn)==2) x q[2];
if(int[2](syn)==3) x q[1];
c = measure q;

View file

@ -0,0 +1,19 @@
// quantum Fourier transform
include "stdgates.inc";
qubit[4] q;
bit[4] c;
reset q;
x q[0];
x q[2];
barrier q;
h q[0];
cphase(pi / 2) q[1], q[0];
h q[1];
cphase(pi / 4) q[2], q[0];
cphase(pi / 2) q[2], q[1];
h q[2];
cphase(pi / 8) q[3], q[0];
cphase(pi / 4) q[3], q[1];
cphase(pi / 2) q[3], q[2];
h q[3];
c = measure q;

View file

@ -0,0 +1,14 @@
include "stdgates.inc";
gate pre q { } // pre-rotation
gate post q { } // post-rotation
qubit q;
bit c;
reset q;
pre q;
barrier q;
h q;
barrier q;
post q;
c = measure q;

View file

@ -0,0 +1,19 @@
// One randomized benchmarking sequence
include "stdgates.inc";
qubit[2] q;
bit[2] c;
reset q;
h q[0];
barrier q;
cz q[0], q[1];
barrier q;
s q[0];
cz q[0], q[1];
barrier q;
s q[0];
z q[0];
h q[0];
barrier q;
measure q -> c;

View file

@ -0,0 +1,39 @@
/*
* Repeat-until-success circuit for Rz(theta),
* cos(theta-pi)=3/5, from Nielsen and Chuang, Chapter 4.
*/
include "stdgates.inc";
/*
* Applies identity if out is 01, 10, or 11 and a Z-rotation by
* theta + pi where cos(theta)=3/5 if out is 00.
* The 00 outcome occurs with probability 5/8.
*/
def segment(qubit[2] anc, qubit psi) -> bit[2] {
bit[2] b;
reset anc;
h anc;
ccx anc[0], anc[1], psi;
s psi;
ccx anc[0], anc[1], psi;
z psi;
h anc;
measure anc -> b;
return b;
}
qubit input_qubit;
qubit[2] ancilla;
bit[2] flags = "11";
bit output_qubit;
reset input_qubit;
h input_qubit;
// braces are optional in this case
while(int[2](flags) != 0) {
flags = segment(ancilla, input_qubit);
}
rz(pi - arccos(3 / 5)) input_qubit;
h input_qubit;
output_qubit = measure input_qubit; // should get zero

View file

@ -0,0 +1,103 @@
/*
* Surface code quantum memory.
*
* Estimate the failure probability as a function
* of parameters at the top of the file.
*/
include "stdgates.inc";
const int[32] d = 3; // code distance
const int[32] m = 10; // number of syndrome measurement cycles
const int[32] shots = 1000; // number of samples
const int[32] n = d**2; // number of code qubits
uint[32] failures; // number of observed failures
extern zfirst(creg[n - 1], int[32], int[32]);
extern send(creg[n -1 ], int[32], int[32], int[32]);
extern zlast(creg[n], int[32], int[32]) -> bit;
qubit[n] data; // code qubits
qubit[n-1] ancilla; // syndrome qubits
/*
* Ancilla are addressed in a (d-1) by (d-1) square array
* followed by 4 length (d-1)/2 arrays for the top,
* bottom, left, and right boundaries.
*/
bit[n-1] layer; // syndrome outcomes in a cycle
bit[n] data_outcomes; // data outcomes at the end
bit outcome; // logical outcome
/* Declare a sub-circuit for Hadamard gates on ancillas
*/
def hadamard_layer(qubit[n-1] ancilla) {
// Hadamards in the bulk
for uint[32] row in [0: d-2] {
for uint[32] col in [0: d-2] {
bit[32] sum = bit[32](row + col);
if(sum[0] == 1)
h ancilla[row * (d - 1) + col];
}
}
// Hadamards on the left and right boundaries
for uint[32] i in [0: d - 2] {
h ancilla[(d - 1)**2 + (d - 1) + i];
}
}
/* Declare a sub-circuit for a syndrome cycle.
*/
def cycle(qubit[n] data, qubit[n-1] ancilla) -> bit[n-1] {
reset ancilla;
hadamard_layer ancilla;
// First round of CNOTs in the bulk
for uint[32] row in [0: d - 2] {
for uint[32] col in [0:d - 2] {
bit[32] sum = bit[32](row + col);
if(sum[0] == 0)
cx data[row * d + col], ancilla[row * (d - 1) + col];
if(sum[0] == 1) {
cx ancilla[row * (d - 1) + col], data[row * d + col];
}
}
}
// First round of CNOTs on the bottom boundary
for uint[32] i in [0: (d - 3) / 2] {
cx data[d * (d - 1) + 2 * i], ancilla[(d - 1) ** 2 + ( d- 1) / 2 + i];
}
// First round of CNOTs on the right boundary
for uint[32] i in [0: (d - 3) / 2] {
cx ancilla[(d - 1) ** 2 + 3 * (d - 1) / 2 + i], data[2 * d - 1 + 2 * d * i];
}
// Remaining rounds of CNOTs, go here ...
hadamard_layer ancilla;
return measure ancilla;
}
// Loop over shots
for uint[32] shot in [1: shots] {
// Initialize
reset data;
layer = cycle(data, ancilla);
zfirst(layer, shot, d);
// m cycles of syndrome measurement
for int[32] i in [1: m] {
layer = cycle(data, ancilla);
send(layer, shot, i, d);
}
// Measure
data_outcomes = measure data;
outcome = zlast(data_outcomes, shot, d);
failures += int[1](outcome);
}
/* The ratio of "failures" to "shots" is our result.
* The data can be logged by the external functions too.
*/

View file

@ -0,0 +1,47 @@
/* Measuring the relaxation time of a qubit
* This example demonstrates the repeated use of fixed delays.
*/
include "stdgates.inc";
duration stride = 1us; // time resolution of points taken
const int[32] points = 50; // number of points taken
const int[32] shots = 1000; // how many shots per point
int[32] counts0;
int[32] counts1 = 0; // surviving |1> populations of qubits
extern tabulate(int[32], int[32], int[32]);
bit c0;
bit c1;
defcalgrammar "openpulse";
// define a gate calibration for an X gate on any qubit
defcal x q {
play drive(q), gaussian(100, 30, 5);
}
for int[32] p in [0 : points-1] {
for uint i in [1 : shots] {
// start of a basic block
reset $0;
reset $1;
// excite qubits
x $0;
x $1;
// wait for a fixed time indicated by loop counter
delay[p * stride] $0;
// wait for a fixed time indicated by loop counters
delay[p * durationof({x $1;})];
// read out qubit states
c0 = measure $0;
c1 = measure $1;
// increment counts memories, if a 1 is seen
counts0 += int[1](c0);
counts1 += int[1](c1);
}
// log survival probability curve
tabulate(counts0, shots, p);
tabulate(counts1, shots, p);
}

View file

@ -0,0 +1,23 @@
// quantum teleportation example
OPENQASM 3; // Version statement is optional
include "stdgates.inc";
qubit[3] q;
bit c0;
bit c1;
bit c2;
// optional post-rotation for state tomography
// empty gate body => identity gate
gate post q { }
reset q;
U(0.3, 0.2, 0.1) q[0];
h q[1];
cx q[1], q[2];
barrier q;
cx q[0], q[1];
h q[0];
c0 = measure q[0];
c1 = measure q[1];
if(c0==1) z q[2];
if(c1==1) { x q[2]; } // braces optional in this case
post q[2];
c2 = measure q[2];

View file

@ -0,0 +1,42 @@
/*
* Prepare a parameterized number of Bell pairs
* and teleport a qubit using them.
*/
include "stdgates.inc";
const int[32] n_pairs = 10; // number of teleportations to do
def bellprep(qubit[2] q) {
reset q;
h q[0];
cx q[0], q[1];
}
def xprepare(qubit q) {
reset q;
h q;
}
qubit input_qubit;
bit output_qubit;
qubit[2*n_pairs] q;
xprepare(input_qubit);
rz(pi / 4) input_qubit;
let io = input_qubit;
for uint i in [0: n_pairs - 1] {
let bp = q[{2*i, 2*i + 1}];
bit[2] pf;
bellprep bp;
cx io, bp[0];
h io;
pf[0] = measure io;
pf[1] = measure bp[0];
if (pf[0]==1) z bp[1];
if (pf[1]==1) x bp[1];
let io = bp[1];
}
h io;
output_qubit = measure io; // should get zero

View file

@ -0,0 +1,88 @@
/*
* Variational eigensolver example
*
* Goal is to estimate the energy for a fixed set of parameters.
* The parameters are updated outside of this program and a new
* OpenQASM circuit is generated for the next iteration.
*/
include "stdgates.inc";
const int[32] n = 10; // number of qubits
const int[32] layers = 3; // number of entangler layers
const int[32] prec = 16; // precision of all types
const int[32] shots = 1000; // number of shots per Pauli observable
// Parameters could be written to local variables for this
// iteration, but we will request them using extern functions
extern get_parameter(uint[prec]) -> angle[prec];
extern get_npaulis() -> uint[prec];
extern get_pauli(int[prec]) -> bit[2 * n];
// The energy calculation uses floating point division,
// so we do that calculation in an extern function
extern update_energy(int[prec], uint[prec], float[prec]) -> float[prec];
gate entangler q { for uint i in [0:n-2] { cx q[i], q[i+1]; } }
def xmeasure(qubit q) -> bit { h q; return measure q; }
def ymeasure(qubit q) -> bit { s q; h q; return measure q; }
/* Pauli measurement circuit.
* The first n-bits of spec are the X component.
* The second n-bits of spec are the Z component.
*/
def pauli_measurement(bit[2*n] spec, qubit[n] q) -> bit {
bit b = 0;
for uint[prec] i in [0: n - 1] {
bit temp;
if(spec[i]==1 && spec[n+i]==0) { temp = xmeasure(q[i]); }
if(spec[i]==0 && spec[n+i]==1) { temp = measure q[i]; }
if(spec[i]==1 && spec[n+i]==1) { temp = ymeasure(q[i]); }
b ^= temp;
}
return b;
}
// Circuit to prepare trial wave function
def trial_circuit(qubit[n] q) {
for int[prec] l in [0: layers - 1] {
for uint[prec] i in [0: n - 1] {
angle[prec] theta;
theta = get_parameter(l * layers + i);
ry(theta) q[i];
}
if(l != layers - 1) entangler q;
}
}
/* Apply VQE ansatz circuit and measure a Pauli operator
* given by spec. Return the number of 1 outcomes.
*/
def counts_for_term(bit[2*n] spec, qubit[n] q) -> uint[prec] {
uint[prec] counts;
for uint i in [1: shots] {
bit b;
reset q;
trial_circuit q;
b = pauli_measurement(spec, q);
counts += int[1](b);
}
return counts;
}
// Estimate the expected energy
def estimate_energy(qubit[n] q) -> float[prec] {
float[prec] energy;
uint[prec] npaulis = get_npaulis();
for int[prec] t in [0:npaulis-1] {
bit[2*n] spec = get_pauli(t);
uint[prec] counts;
counts = counts_for_term(spec, q);
energy = update_energy(t, counts, energy);
}
return energy;
}
qubit[n] q;
float[prec] energy;
energy = estimate_energy(q);

View file

@ -6,6 +6,8 @@ An editor for OpenQASM programs, with three places to run them:
- **this computer**, through the same engine built natively, in the [desktop app](../wai-quantum-desktop/);
- **the job service**, over HTTP.
In the desktop app, **your hardware** is a fourth: one of your own providers (`wai-quantum providers add`), its estimate asked for first and a dialog shown before a run that is not free, the credential held by the app and never by the page. In a browser tab the switch only says that such runs go through the desktop app or the command line (`wai-quantum run --provider NAME`), since a browser tab must not hold a provider's credential.
Every place returns the same job: the request, the method and why it was chosen, the result, and its receipt. The Studio shows them the same way and checks every receipt on the machine it runs on. The same request with the same seed gives the same counts in each place.
The editor:
@ -19,6 +21,11 @@ Ctrl+Enter (⌘+Enter on macOS) runs the program, and Escape cancels a run. The
## Generators
The editor reads a program with the engine's own reader, the one every
surface uses: the whole of OpenQASM 3.1, each problem marked on its line with
the reader's code (`[Q103]`); `engine_diagnostic_codes()` gives each code's
meaning.
A program that declares integer inputs (`input uint n;`) is a generator: its `for` loops and its `if`s on integers write out one circuit for each value. The Studio shows a field for each input under the editor and reads the program at those values. The lint and the critique read the generator itself, so a problem or a smell keeps the generator's own line. The diagram, the plan, a run (here or on the service), the lens and the alternatives take the circuit it writes out. The values are kept in this browser, by name.
## The Workbench

View file

@ -0,0 +1,113 @@
// Your own hardware: a run on one of the person's own quantum providers (a
// device, an account, a simulator), in the desktop app only.
//
// A provider needs the person's credential, and a browser tab must not hold
// one, so in the browser the "Your hardware" switch only says where such a
// run goes: the desktop app, or the command line (`wai-quantum run
// --provider NAME`). In the desktop app the app holds the credential (in the
// operating system's keychain) and the page never sees it: the page lists
// the providers, asks for the provider's estimate first, and when the run is
// not free asks the person in a dialog before it is submitted. The answer is
// a job like every other, its counts measured on the device and sealed.
import { el } from './dom.js';
export const NOTE = 'Runs on your own hardware go through the desktop app or the command line (wai-quantum run --provider NAME): a browser tab must not hold the credentials a provider needs.';
// Whether a plan (the dry run's answer) needs the person's confirmation: any
// run not stated free, an unknown cost included.
export const needsConfirmation = (plan) => plan?.free !== true;
// The estimate in words.
export function costText(plan) {
const e = plan?.estimate || {};
if (e.unknown) return `cost unknown: ${e.unknown}`;
if (e.free === true) return 'free';
const unit = e.unit ? ` (billed by the ${e.unit})` : '';
const secs = e.seconds !== undefined ? `, about ${e.seconds} s` : '';
return `${e.amount} ${e.currency}${unit}${secs}`;
}
// What the dialog says before a run that is not free.
export function confirmText(plan) {
const p = plan?.provider || {};
const c = plan?.plan?.compile || {};
return [
`Run on ${p.name} (${p.device}, ${p.provenance})?`,
`It is not free: ${costText(plan)}.`,
`The program is compiled to ${c.native_gates} native gates in ${c.basis}, ${c.swaps} swaps, and was checked against your circuit here; ${plan?.plan?.shots} shots.`,
'Running it spends your account\'s money or quota.',
];
}
// A run on a provider, through the desktop app's commands.
export class HardwareEngine {
constructor(invoke, ask) {
this.invoke = invoke;
this.ask = ask;
this.provider = null;
this.where = 'your hardware';
}
async call(cmd, args) {
const v = JSON.parse(await this.invoke(cmd, args));
if (v && v.error && !v.plan) throw new Error(v.error);
return v;
}
async providers() { return (await this.call('providers_list', {})).providers || []; }
select(name) { this.provider = name; this.where = name ? `your provider ${name}` : 'your hardware'; }
estimate(request) { return this.call('provider_estimate', { name: this.provider, request: JSON.stringify({ ...request, provider: this.provider }) }); }
// The estimate first; the person's yes before anything that is not free.
async run(request) {
if (!this.provider) throw new Error('choose one of your providers first');
const req = { qasm: request.qasm, shots: request.shots, seed: request.seed, provider: this.provider };
const plan = await this.estimate(req);
let confirm = false;
if (needsConfirmation(plan)) {
if (!(await this.ask(plan))) throw new Error('cancelled');
confirm = true;
}
const v = await this.call('run_on_provider', { name: this.provider, request: JSON.stringify(req), confirmCost: confirm });
if (v.error) throw new Error(v.error);
return v;
}
methods() { return Promise.resolve({ methods: [] }); }
cancel() {}
}
// A dialog that asks before a run that is not free: resolves true on "Run".
export function askDialog(host) {
return (plan) => new Promise((resolve) => {
const yes = el('button', { class: 'qs-btn qs-primary', type: 'button', id: 'qs-hw-yes', text: 'Run it' });
const no = el('button', { class: 'qs-btn', type: 'button', id: 'qs-hw-no', text: 'Cancel' });
const d = el('dialog', { class: 'qs-dialog', id: 'qs-hw-confirm', 'aria-labelledby': 'qs-hw-confirm-title' },
el('h2', { id: 'qs-hw-confirm-title', text: 'This run is not free' }),
...confirmText(plan).map((t) => el('p', { text: t })),
el('div', { class: 'qs-row' }, no, yes));
const end = (v) => { d.close(); d.remove(); resolve(v); };
yes.addEventListener('click', () => end(true));
no.addEventListener('click', () => end(false));
d.addEventListener('cancel', (e) => { e.preventDefault(); end(false); });
host.append(d);
d.showModal();
no.focus();
});
}
// The switch's segment and, on the desktop, the provider picker. `onPick`
// is told when the person picks this place to run.
export function hardwareSegment({ desktop, engine, onPick, say }) {
const seg = el('button', { class: 'qs-seg', type: 'button', 'aria-pressed': 'false', 'data-where': 'hardware', id: 'qs-where-hardware', text: 'Your hardware', title: desktop ? 'Run on one of your own providers; a run that is not free asks first' : NOTE });
const picker = el('select', { class: 'qs-select', id: 'qs-provider', 'aria-label': 'Your provider', hidden: true });
seg.addEventListener('click', async () => {
if (!desktop) { say(NOTE); return; }
try {
const list = await engine.providers();
if (!list.length) { say('No providers yet: add one with wai-quantum providers add, then reopen this list.'); return; }
picker.replaceChildren(...list.map((p) => el('option', { value: p.name, text: `${p.name} (${p.kind})` })));
picker.hidden = false;
engine.select(picker.value);
onPick();
} catch (e) { say(e.message); }
});
picker.addEventListener('change', () => engine.select(picker.value));
return { seg, picker };
}

View file

@ -43,6 +43,7 @@ import { declared, inputsBar, withInputs } from './generators.js';
import { shortcutsPanel } from './shortcuts-view.js';
import { rewritePanel } from './rewrite-view.js';
import * as Predict from './predict.js';
import { HardwareEngine, askDialog, hardwareSegment } from './hardware.js';
const METHOD_NAMES = {
exact: 'Exact state vector', statevector: 'State vector, double precision', statevector_single: 'State vector on the GPU, single precision',
@ -92,6 +93,8 @@ function mount(root) {
const inputs = inputsBar(() => { view.dispatch({ effects: inputsChanged.of(null) }); forceLinting(view); edited(); });
const local = withInputs(native, inputs.values);
const service = root.dataset.service ? withInputs(new ServiceEngine(root.dataset.service), inputs.values, native) : null;
// The person's own providers: in the desktop app only, which holds their credentials.
const hardware = desktop ? withInputs(new HardwareEngine(window.__TAURI__.core.invoke, askDialog(root)), inputs.values, native) : null;
let where = 'local';
let current = null; // {job, where, took}
let running = null;
@ -108,12 +111,13 @@ function mount(root) {
const predictLabel = el('label', { class: 'qs-check', for: 'qs-predict-first', title: 'Say what you expect before each run, and see how close you come' }, predictFirst, el('span', { text: 'Predict first' }));
const whereLocal = el('button', { class: 'qs-seg on', type: 'button', 'aria-pressed': 'true', 'data-where': 'local', text: desktop ? 'This computer' : 'This browser' });
const whereService = el('button', { class: 'qs-seg', type: 'button', 'aria-pressed': 'false', 'data-where': 'service', text: 'The service', disabled: !service });
const hw = hardwareSegment({ desktop, engine: hardware, onPick: () => choose(hw.seg), say: (m) => setStatus(m) });
const examples = el('select', { class: 'qs-select', id: 'qs-example', 'aria-label': 'Load an example' },
el('option', { value: '', text: 'Examples…' }), ...EXAMPLES.map((e) => el('option', { value: e.id, text: e.title })));
const header = el('header', { class: 'qs-top' },
el('div', { class: 'qs-brand' }, el('span', { class: 'qs-mark', 'aria-hidden': 'true', text: '⟨ψ⟩' }), el(embedded ? 'span' : 'h1', { class: 'qs-title', text: 'Quantum Studio' })),
examples,
el('div', { class: 'qs-where', role: 'group', 'aria-label': 'Run on' }, whereLocal, whereService),
el('div', { class: 'qs-where', role: 'group', 'aria-label': 'Run on' }, whereLocal, whereService, hw.seg), hw.picker,
predictLabel, runBtn, cancelBtn);
// ---- the reader's rules for proposals ----
@ -484,7 +488,7 @@ function mount(root) {
async function go(prediction) {
if (running) return;
const job = request();
const eng = where === 'service' ? service : local;
const eng = where === 'service' ? service : where === 'hardware' ? hardware : local;
running = eng; busy(true);
state.textContent = 'running'; state.className = 'qs-badge run';
setStatus(`Running on ${eng.where}…`);
@ -727,11 +731,14 @@ function mount(root) {
}
// ---- where ----
for (const b of [whereLocal, whereService]) b.addEventListener('click', () => {
const places = [whereLocal, whereService, hw.seg];
function choose(b) {
where = b.dataset.where;
for (const x of [whereLocal, whereService]) { x.classList.toggle('on', x === b); x.setAttribute('aria-pressed', String(x === b)); }
for (const x of places) { x.classList.toggle('on', x === b); x.setAttribute('aria-pressed', String(x === b)); }
hw.picker.hidden = where !== 'hardware';
loadMethods(); plan();
});
}
for (const b of [whereLocal, whereService]) b.addEventListener('click', () => choose(b));
async function loadMethods() {
const keep = method.value;
try {

View file

@ -60,6 +60,10 @@
.qs-seg + .qs-seg { border-left: 1px solid var(--border-subtle); }
.qs-seg.on { background: color-mix(in srgb, var(--accent) 18%, var(--bg-base)); color: var(--text-primary); }
.qs-seg:disabled { opacity: 0.45; cursor: default; }
.qs-dialog { max-width: 32rem; padding: 16px 18px; border: 1px solid var(--border-subtle); border-radius: 8px; background: var(--bg-base); color: var(--text-primary); font: 13px/1.5 var(--font-sans, system-ui, sans-serif); }
.qs-dialog h2 { margin: 0 0 8px; font-size: 15px; }
.qs-dialog .qs-row { justify-content: flex-end; margin-top: 12px; }
.qs-dialog::backdrop { background: rgb(0 0 0 / 0.45); }
.qs :focus-visible { outline: 2px solid var(--focus-ring, var(--accent)); outline-offset: 2px; }
.qs-badge { display: inline-block; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border-subtle); font: 11px var(--font-mono, ui-monospace, monospace); color: var(--text-secondary); white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.qs-badge.ok { color: var(--ok-text); border-color: color-mix(in srgb, var(--ok-text) 40%, transparent); }

View file

@ -0,0 +1,70 @@
// The "Your hardware" logic, without a browser: a run that is not free asks
// the person first, a free one does not, a "no" sends nothing, and the
// dialog's words carry the estimate.
//
// node test/hardware.test.mjs
import { HardwareEngine, NOTE, confirmText, costText, needsConfirmation } from '../src/hardware.js';
let fails = 0;
const check = (ok, what) => { console.log(`${ok ? 'PASS' : 'FAIL'} ${what}`); if (!ok) fails += 1; };
const plan = (free, estimate) => ({ free, estimate, provider: { name: 'lab', device: 'reference:line-5', provenance: 'simulator:x' }, plan: { shots: 100, compile: { native_gates: 12, basis: 'rzsxxcz', swaps: 0 } } });
const priced = plan(false, { free: false, amount: '0.3', currency: 'USD', unit: 'shot', seconds: 4 });
check(needsConfirmation(priced) && !needsConfirmation(plan(true, { free: true })), 'a run not stated free needs a confirmation, a free one does not');
check(needsConfirmation(plan(undefined, { unknown: 'no estimate' })), 'an unknown cost needs a confirmation');
check(costText(priced) === '0.3 USD (billed by the shot), about 4 s', `the cost in words (${costText(priced)})`);
check(costText(plan(false, { unknown: 'the provider gives no estimate' })) === 'cost unknown: the provider gives no estimate', 'an unknown cost says so');
const words = confirmText(priced).join(' ');
check(/Run on lab \(reference:line-5/.test(words) && /not free: 0\.3 USD/.test(words) && /12 native gates in rzsxxcz/.test(words) && /spends your account/.test(words), `the dialog's words (${words})`);
check(/desktop app or the command line/.test(NOTE) && /must not hold the credentials/.test(NOTE), 'the browser note');
// The engine against fake desktop commands.
function fake(p) {
const calls = [];
const invoke = async (cmd, args) => {
calls.push([cmd, args]);
if (cmd === 'providers_list') return JSON.stringify({ providers: [{ name: 'lab', kind: 'process' }] });
if (cmd === 'provider_estimate') return JSON.stringify(p);
if (cmd === 'run_on_provider') return JSON.stringify({ status: 'done', method: 'provider', request: JSON.parse(args.request), result: { counts: { '00': 50, '11': 50 } } });
return JSON.stringify({ error: `no command ${cmd}` });
};
return { calls, invoke };
}
{
const f = fake(plan(true, { free: true }));
let asked = 0;
const e = new HardwareEngine(f.invoke, async () => { asked += 1; return true; });
check((await e.providers()).length === 1, 'the providers are listed');
e.select('lab');
const body = await e.run({ qasm: 'OPENQASM 3.0; qubit[2] q;', shots: 100 });
const run = f.calls.find((c) => c[0] === 'run_on_provider');
check(asked === 0 && run[1].confirmCost === false && body.request.provider === 'lab', 'a free run is not asked about, and names its provider');
}
{
const f = fake(priced);
const e = new HardwareEngine(f.invoke, async () => false);
e.select('lab');
let err = '';
try { await e.run({ qasm: 'q', shots: 100 }); } catch (x) { err = x.message; }
check(err === 'cancelled' && !f.calls.some((c) => c[0] === 'run_on_provider'), 'a run not free, declined, sends nothing');
}
{
const f = fake(priced);
let shown = null;
const e = new HardwareEngine(f.invoke, async (p) => { shown = p; return true; });
e.select('lab');
await e.run({ qasm: 'q', shots: 100 });
const run = f.calls.find((c) => c[0] === 'run_on_provider');
check(shown === null ? false : shown.estimate.amount === '0.3', 'the dialog is shown the estimate');
check(run && run[1].confirmCost === true, 'a run not free, accepted, is sent confirmed');
}
{
const e = new HardwareEngine(fake(priced).invoke, async () => true);
let err = '';
try { await e.run({ qasm: 'q' }); } catch (x) { err = x.message; }
check(/choose one of your providers/.test(err), 'no provider chosen, no run');
}
if (fails) { console.error(`${fails} failed`); process.exit(1); }
console.log('hardware: every check held');

View file

@ -7,6 +7,8 @@
// - the first example loads; the program is linted, drawn and planned as it is typed;
// - it runs in this browser: the stabilizer tableau, only 00 and 11, ⟨Z0 Z1⟩ = 1, the
// receipt checked here and signed by this browser's key;
// - "Your hardware" says, in a browser tab, that such a run goes through the desktop
// app or the command line, since a tab must not hold a provider's credential;
// - an error in the program is marked on its line;
// - a rotation runs on the state vector, ⟨Z0⟩ = cos 0.3;
// - the same Bell job on the service gives the counts run here, shot for shot, and its
@ -126,6 +128,10 @@ check(zz.some((t) => t.startsWith('⟨Z0 Z1⟩') && t.includes('1.000000')), `
check((await page.textContent('#qs-verified')).startsWith('✓ verifies'), `the receipt verifies, checked here (${await page.textContent('#qs-verified')})`);
check((await page.textContent('#qs-signed-by')).startsWith('this browser’s key did:key:z6Mk'), `signed by this browser's key (${await page.textContent('#qs-signed-by')})`);
check((await page.$$('.qs-hitem')).length === 1, 'the run is in the history');
// Your own hardware: in a browser tab the switch says where such a run goes, and stays here.
await page.click('#qs-where-hardware');
check(/desktop app or the command line/.test(await page.textContent('.qs-status')) && /a browser tab must not hold the credentials/.test(await page.textContent('.qs-status')), `“Your hardware” says a run on your own hardware goes through the desktop app or the command line (${await page.textContent('.qs-status')})`);
check((await page.getAttribute('[data-where=local]', 'aria-pressed')) === 'true' && await page.isHidden('#qs-provider'), 'and the Studio still runs in this browser');
// What it looks like, for a reader of the change: the dark scheme and the light.
mkdirSync(SHOTS, { recursive: true });
await page.screenshot({ path: join(SHOTS, 'studio-dark.png'), fullPage: true });
@ -727,7 +733,7 @@ const wbIdle = () => page.waitForFunction(() => !document.querySelector('#qs-wb-
await page.waitForSelector('.qs-ai-apply', { timeout: LONG });
const log = await page.$$eval('#qs-ai-log > li', (ls) => ls.map((l) => l.textContent));
check(log.some((t) => t.includes('used lint_qasm')), 'the model\'s tool call ran on the engine, and the log shows it');
check(log.some((t) => /could not read the proposed program \(line 3: unknown gate foo\)/.test(t)), `a proposal the engine cannot read goes back with the engine's reason (${log.find((t) => /could not read/.test(t)) ?? 'none'})`);
check(log.some((t) => /could not read the proposed program \(line 3: unknown gate foo \[Q103\]\)/.test(t)), `a proposal the engine cannot read goes back with the engine's reason (${log.find((t) => /could not read/.test(t)) ?? 'none'})`);
const proposal = await page.textContent('.qs-ai-proposal');
check(/checked: 2 qubits, 2 gates/.test(proposal) && /ran here on Stabilizer tableau/.test(proposal), `the corrected proposal is checked and run before it is offered (${proposal})`);
check(asked.length === 3 && asked.every((r) => r.tools?.some((t) => t.function.name === 'run_job') && r.messages[0].role === 'system' && /Never state a probability/.test(r.messages[0].content)), 'every call to the model carries the engine\'s tools and the rule that numbers come from them');

View file

@ -0,0 +1,5 @@
node_modules/
build/
*.so
*.dylib
*.wasm

View file

@ -0,0 +1,41 @@
# tree-sitter-openqasm
OpenQASM 3, and the OpenQASM 2 forms in common use, as a tree-sitter grammar:
for highlighting, folding and structural selection in any editor that reads
tree-sitter grammars. The language service (`wai-quantum lsp`) gives the
engine's diagnostics, completion, hover and renames; this grammar gives the
editor the program's structure as it is typed.
| Path | What it is |
|---|---|
| `grammar.js` | The grammar: every statement of OpenQASM 3.1 (declarations of every type, gates, subroutines, modifiers, control flow, timing, calibrations kept as text, pragmas, annotations) and OpenQASM 2's `qreg`, `creg`, `opaque`, `measure q -> c` and `if(c==1)` |
| `src/` | The parser generated from it (ABI 14), committed so that an editor builds it with a C compiler alone |
| `queries/highlights.scm` | Highlighting, in the common capture names (`@keyword`, `@type`, `@function`, `@number`, …) |
| `queries/locals.scm` | Scopes, definitions and references |
| `queries/folds.scm` | Foldable blocks |
| `test/corpus/` | The parse of every construct, checked tree for tree |
| `test/highlight/` | Highlighting checked capture by capture |
| `script/check-programs.mjs` | Parses every program in the repository's conformance material and examples, and fails on any error |
The grammar reads what is written. It does not say what a program means: the
engine's reader decides that, and the language server reports it.
## Building and testing
With Node.js 22 and a C compiler:
```sh
npm ci # the pinned tree-sitter command line
npx tree-sitter generate --abi 14 # src/ from grammar.js
npx tree-sitter test # the corpus and the highlighting
node script/check-programs.mjs # every program in the repository
```
`src/` must be regenerated and committed with any change to `grammar.js`; CI
regenerates it and fails if the committed parser differs.
## Using it in an editor
The editor configurations in [`../wai-quantum-editor/editors/`](../wai-quantum-editor/editors/)
point at this directory, and the [editors guide](../wai-quantum-editor/README.md#other-editors)
says how to install each.

View file

@ -0,0 +1,406 @@
/**
* @file OpenQASM 3, and the OpenQASM 2 forms in common use, for tree-sitter.
* @license Apache-2.0
*
* The grammar reads what is written: every statement of OpenQASM 3.1
* (declarations of every type, gates and subroutines, modifiers, control
* flow, timing, calibrations, pragmas and annotations) and OpenQASM 2's
* `qreg`, `creg`, `opaque`, `measure q -> c` and `if(c==1)`. It does not say
* what a program means; the engine's reader does.
*/
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const PREC = {
logical_or: 1,
logical_and: 2,
bit_or: 3,
bit_xor: 4,
bit_and: 5,
equality: 6,
comparison: 7,
shift: 8,
additive: 9,
multiplicative: 10,
unary: 11,
power: 12,
call: 13,
index: 14,
};
const DECIMAL = /[0-9](_?[0-9])*/;
const FLOAT = choice(
seq(DECIMAL, '.', optional(DECIMAL), optional(/[eE][+-]?[0-9]+/)),
seq('.', DECIMAL, optional(/[eE][+-]?[0-9]+/)),
seq(DECIMAL, /[eE][+-]?[0-9]+/),
);
/**
* One or more of `rule`, separated by commas, with an optional trailing comma.
* @param {RuleOrLiteral} rule
*/
function commaSep1(rule) {
return seq(rule, repeat(seq(',', rule)), optional(','));
}
module.exports = grammar({
name: 'openqasm',
extras: $ => [/\s/, $.line_comment, $.block_comment],
word: $ => $.identifier,
conflicts: $ => [
// After a name and `[`: `g[100ns] q;` calls a gate for a duration,
// `c[0] = …` assigns, and `c[0];` is an expression.
[$.gate_call, $.indexed_identifier, $._expression],
// `rx(θ)[100ns] q;` calls a gate for a duration; `f(x)[0];` indexes a call's value.
[$.gate_call, $.call_expression],
],
rules: {
source_file: $ => seq(optional($.version), repeat($._statement_or_scope)),
version: $ => seq('OPENQASM', field('number', choice($.integer_literal, $.float_literal)), ';'),
_statement_or_scope: $ => choice($._statement, $.scope),
scope: $ => seq('{', repeat($._statement_or_scope), '}'),
_statement: $ => choice(
$.pragma,
$.annotation,
$.include,
$.calibration_grammar,
$.quantum_declaration,
$.old_style_declaration,
$.classical_declaration,
$.const_declaration,
$.io_declaration,
$.alias_declaration,
$.gate_definition,
$.opaque_declaration,
$.subroutine_definition,
$.extern_declaration,
$.calibration,
$.calibration_definition,
$.gate_call,
$.gphase_call,
$.measure_statement,
$.reset_statement,
$.barrier_statement,
$.delay_statement,
$.box_statement,
$.assignment_statement,
$.expression_statement,
$.if_statement,
$.for_statement,
$.while_statement,
$.switch_statement,
$.return_statement,
$.break_statement,
$.continue_statement,
$.end_statement,
),
pragma: _ => token(seq(choice('#pragma', 'pragma'), /[^\n]*/)),
annotation: _ => token(seq('@', /[A-Za-z_][A-Za-z0-9_.]*/, /[^\n]*/)),
include: $ => seq('include', field('path', $.string), ';'),
calibration_grammar: $ => seq('defcalgrammar', field('name', $.string), ';'),
// Declarations.
quantum_declaration: $ => seq(field('type', $.qubit_type), field('name', $.identifier), ';'),
qubit_type: $ => seq('qubit', optional($.designator)),
old_style_declaration: $ => seq(choice('qreg', 'creg'), field('name', $.identifier), optional($.designator), ';'),
classical_declaration: $ => seq(
field('type', choice($.scalar_type, $.array_type)),
field('name', $.identifier),
optional(seq('=', field('value', $._declaration_value))),
';',
),
const_declaration: $ => seq('const', field('type', $.scalar_type), field('name', $.identifier), '=', field('value', $._declaration_value), ';'),
io_declaration: $ => seq(choice('input', 'output'), field('type', choice($.scalar_type, $.array_type)), field('name', $.identifier), ';'),
alias_declaration: $ => seq('let', field('name', $.identifier), '=', field('value', $.alias_expression), ';'),
_declaration_value: $ => choice($._expression, $.array_literal, $.measure_expression),
designator: $ => seq('[', $._expression, ']'),
scalar_type: $ => choice(
seq(choice('bit', 'int', 'uint', 'float', 'angle'), optional($.designator)),
'bool',
'duration',
'stretch',
seq('complex', optional(seq('[', $.scalar_type, ']'))),
),
array_type: $ => seq('array', '[', $.scalar_type, ',', commaSep1($._expression), ']'),
array_reference_type: $ => seq(
choice('readonly', 'mutable'),
'array',
'[',
$.scalar_type,
',',
choice(commaSep1($._expression), seq('#dim', '=', $._expression)),
']',
),
// Gates, subroutines and calibrations.
gate_definition: $ => seq(
'gate',
field('name', $.identifier),
optional(field('parameters', $.gate_parameters)),
field('qubits', $.identifier_list),
field('body', $.scope),
),
opaque_declaration: $ => seq('opaque', field('name', $.identifier), optional(field('parameters', $.gate_parameters)), field('qubits', $.identifier_list), ';'),
gate_parameters: $ => seq('(', optional(commaSep1($.identifier)), ')'),
identifier_list: $ => commaSep1($.identifier),
subroutine_definition: $ => seq(
'def',
field('name', $.identifier),
field('parameters', $.parameter_list),
optional(field('return_type', $.return_signature)),
field('body', $.scope),
),
parameter_list: $ => seq('(', optional(commaSep1($.parameter)), ')'),
parameter: $ => choice(
seq(field('type', choice($.scalar_type, $.qubit_type, $.array_reference_type)), field('name', $.identifier)),
seq(choice('creg', 'qreg'), field('name', $.identifier), optional($.designator)),
),
return_signature: $ => seq('->', $.scalar_type),
extern_declaration: $ => seq(
'extern',
field('name', $.identifier),
'(',
optional(commaSep1(choice($.scalar_type, $.array_reference_type, seq('creg', optional($.designator))))),
')',
optional(field('return_type', $.return_signature)),
';',
),
calibration: $ => seq('cal', field('body', $.calibration_block)),
calibration_definition: $ => seq(
'defcal',
field('target', choice('measure', 'reset', 'delay', $.identifier)),
optional(seq('(', optional(commaSep1(choice($.parameter, $._expression))), ')')),
commaSep1(choice($.hardware_qubit, $.identifier)),
optional(field('return_type', $.return_signature)),
field('body', $.calibration_block),
),
// A calibration's body is in its own grammar: kept as text, its braces matched.
calibration_block: $ => seq('{', repeat(choice($.calibration_text, $.calibration_block)), '}'),
calibration_text: _ => token(prec(-1, /[^{}\s][^{}]*/)),
// Quantum statements.
gate_call: $ => seq(
repeat($.gate_modifier),
field('name', $.identifier),
optional(field('arguments', $.argument_list)),
optional(field('duration', $.designator)),
field('operands', $.operand_list),
';',
),
gphase_call: $ => seq(
repeat($.gate_modifier),
'gphase',
optional(field('arguments', $.argument_list)),
optional(field('duration', $.designator)),
optional(field('operands', $.operand_list)),
';',
),
gate_modifier: $ => seq(
choice(
'inv',
seq('pow', '(', $._expression, ')'),
seq(choice('ctrl', 'negctrl'), optional(seq('(', $._expression, ')'))),
),
'@',
),
operand_list: $ => commaSep1($._operand),
_operand: $ => choice($.indexed_identifier, $.hardware_qubit),
indexed_identifier: $ => prec.left(seq($.identifier, repeat($.index))),
index: $ => seq('[', choice($.set_expression, commaSep1(choice($._expression, $.range_expression))), ']'),
measure_statement: $ => seq($.measure_expression, optional(seq('->', field('target', $.indexed_identifier))), ';'),
measure_expression: $ => seq('measure', field('operand', $._operand)),
reset_statement: $ => seq('reset', field('operand', $._operand), ';'),
barrier_statement: $ => seq('barrier', optional($.operand_list), ';'),
delay_statement: $ => seq('delay', field('duration', $.designator), optional($.operand_list), ';'),
box_statement: $ => seq('box', optional(field('duration', $.designator)), field('body', $.scope)),
// Classical statements.
assignment_statement: $ => seq(
field('left', $.indexed_identifier),
field('operator', choice('=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '~=', '<<=', '>>=', '**=')),
field('right', choice($._expression, $.measure_expression)),
';',
),
expression_statement: $ => seq($._expression, ';'),
if_statement: $ => prec.right(seq(
'if',
'(',
field('condition', $._expression),
')',
field('consequence', $._statement_or_scope),
optional(seq('else', field('alternative', $._statement_or_scope))),
)),
for_statement: $ => seq(
'for',
optional(field('type', $.scalar_type)),
field('variable', $.identifier),
'in',
field('iterable', choice($.set_expression, $.range, $.identifier)),
field('body', $._statement_or_scope),
),
range: $ => seq('[', $.range_expression, ']'),
while_statement: $ => seq('while', '(', field('condition', $._expression), ')', field('body', $._statement_or_scope)),
switch_statement: $ => seq('switch', '(', field('value', $._expression), ')', '{', repeat(choice($.case_clause, $.default_clause)), '}'),
case_clause: $ => seq('case', commaSep1($._expression), field('body', $.scope)),
default_clause: $ => seq('default', field('body', $.scope)),
return_statement: $ => seq('return', optional(choice($._expression, $.measure_expression)), ';'),
break_statement: _ => seq('break', ';'),
continue_statement: _ => seq('continue', ';'),
end_statement: _ => seq('end', ';'),
// Expressions.
_expression: $ => choice(
$.identifier,
$.hardware_qubit,
$.integer_literal,
$.float_literal,
$.imaginary_literal,
$.timing_literal,
$.boolean_literal,
$.string,
$.parenthesized_expression,
$.index_expression,
$.call_expression,
$.cast_expression,
$.durationof_expression,
$.unary_expression,
$.binary_expression,
),
parenthesized_expression: $ => seq('(', $._expression, ')'),
index_expression: $ => prec(PREC.index, seq(field('value', $._expression), $.index)),
call_expression: $ => seq(field('function', $.identifier), field('arguments', $.argument_list)),
argument_list: $ => seq('(', optional(commaSep1($._expression)), ')'),
cast_expression: $ => prec(PREC.call, seq(field('type', choice($.scalar_type, $.array_type)), '(', $._expression, ')')),
durationof_expression: $ => seq('durationof', '(', $.scope, ')'),
unary_expression: $ => prec(PREC.unary, seq(field('operator', choice('-', '+', '!', '~')), field('operand', $._expression))),
binary_expression: $ => {
/** @type {[string[], number, 'left' | 'right'][]} */
const table = [
[['**'], PREC.power, 'right'],
[['*', '/', '%'], PREC.multiplicative, 'left'],
[['+', '-'], PREC.additive, 'left'],
[['<<', '>>'], PREC.shift, 'left'],
[['<', '>', '<=', '>='], PREC.comparison, 'left'],
[['==', '!='], PREC.equality, 'left'],
[['&'], PREC.bit_and, 'left'],
[['^'], PREC.bit_xor, 'left'],
[['|'], PREC.bit_or, 'left'],
[['&&'], PREC.logical_and, 'left'],
[['||'], PREC.logical_or, 'left'],
];
return choice(...table.map(([ops, p, assoc]) => {
const op = ops.length === 1 ? ops[0] : choice(...ops);
const rule = seq(field('left', $._expression), field('operator', op), field('right', $._expression));
return assoc === 'right' ? prec.right(p, rule) : prec.left(p, rule);
}));
},
alias_expression: $ => seq($._expression, repeat(seq('++', $._expression))),
range_expression: $ => prec.right(seq(
optional(field('start', $._expression)),
':',
optional(field('step_or_end', $._expression)),
optional(seq(':', optional(field('end', $._expression)))),
)),
set_expression: $ => seq('{', commaSep1($._expression), '}'),
array_literal: $ => seq('{', optional(commaSep1(choice($._expression, $.array_literal))), '}'),
// Literals and names.
integer_literal: _ => token(choice(/0[bB][01](_?[01])*/, /0[oO][0-7](_?[0-7])*/, /0[xX][0-9a-fA-F](_?[0-9a-fA-F])*/, DECIMAL)),
float_literal: _ => token(FLOAT),
imaginary_literal: _ => token(seq(choice(FLOAT, DECIMAL), /[ \t]*/, 'im')),
timing_literal: _ => token(seq(choice(FLOAT, DECIMAL), choice('dt', 'ns', 'us', 'µs', 'ms', 's'))),
boolean_literal: _ => choice('true', 'false'),
string: _ => token(choice(/"[^"\n]*"/, /'[^'\n]*'/)),
hardware_qubit: _ => /\$[0-9]+/,
identifier: _ => /[\p{L}_][\p{L}\p{N}_]*/u,
line_comment: _ => token(seq('//', /[^\n]*/)),
block_comment: _ => token(seq('/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/')),
},
});

View file

@ -0,0 +1,30 @@
{
"name": "tree-sitter-openqasm",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tree-sitter-openqasm",
"version": "0.1.0",
"license": "Apache-2.0",
"devDependencies": {
"tree-sitter-cli": "0.26.13"
}
},
"node_modules/tree-sitter-cli": {
"version": "0.26.13",
"resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.26.13.tgz",
"integrity": "sha512-SIC74lxT2jsygxaIGGb50Bpf8jtll24aXju98Y3eJienBTXCRQ+5k3YigwN31pASEUZKcKh98EPSZWky7ugUMA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"tree-sitter": "cli.js"
},
"engines": {
"node": ">=12.0.0"
}
}
}
}

View file

@ -0,0 +1,20 @@
{
"name": "tree-sitter-openqasm",
"version": "0.1.0",
"description": "OpenQASM 3, and the OpenQASM 2 forms in common use, for tree-sitter: the grammar, its generated parser, and highlighting and scope queries",
"license": "Apache-2.0",
"private": true,
"repository": {
"type": "git",
"url": "https://git.transaction.science/Transaction-Science/open-standards",
"directory": "wai/tools/tree-sitter-openqasm"
},
"files": ["grammar.js", "tree-sitter.json", "src", "queries"],
"scripts": {
"generate": "tree-sitter generate --abi 14",
"test": "tree-sitter test && node script/check-programs.mjs"
},
"devDependencies": {
"tree-sitter-cli": "0.26.13"
}
}

View file

@ -0,0 +1,8 @@
; Foldable blocks.
[
(scope)
(calibration_block)
(switch_statement)
(block_comment)
] @fold

View file

@ -0,0 +1,170 @@
; OpenQASM highlighting. Captures in the common names; an editor maps them to
; its theme.
(line_comment) @comment
(block_comment) @comment
(pragma) @keyword.directive
(annotation) @attribute
[
"OPENQASM"
"include"
"defcalgrammar"
"gate"
"opaque"
"def"
"extern"
"cal"
"defcal"
"let"
"const"
"input"
"output"
"readonly"
"mutable"
"#dim"
"if"
"else"
"for"
"in"
"while"
"switch"
"case"
"default"
"return"
"break"
"continue"
"end"
"box"
"durationof"
"measure"
"reset"
"barrier"
"delay"
"gphase"
"inv"
"pow"
"ctrl"
"negctrl"
] @keyword
[
"qubit"
"qreg"
"creg"
"bit"
"int"
"uint"
"float"
"angle"
"bool"
"complex"
"array"
"duration"
"stretch"
] @type
(boolean_literal) @boolean
[
(integer_literal)
(imaginary_literal)
(timing_literal)
] @number
(float_literal) @number.float
(string) @string
(calibration_text) @string.special
(hardware_qubit) @variable.builtin
((identifier) @constant.builtin
(#any-of? @constant.builtin "pi" "π" "tau" "τ" "euler" "ℯ"))
(gate_definition
name: (identifier) @function)
(opaque_declaration
name: (identifier) @function)
(subroutine_definition
name: (identifier) @function)
(extern_declaration
name: (identifier) @function)
(calibration_definition
target: (identifier) @function)
(gate_call
name: (identifier) @function.call)
(call_expression
function: (identifier) @function.call)
(gate_parameters
(identifier) @variable.parameter)
(parameter
name: (identifier) @variable.parameter)
(const_declaration
name: (identifier) @constant)
[
"="
"+="
"-="
"*="
"/="
"%="
"&="
"|="
"^="
"~="
"<<="
">>="
"**="
"+"
"-"
"*"
"/"
"%"
"**"
"=="
"!="
"<"
">"
"<="
">="
"&&"
"||"
"!"
"~"
"&"
"|"
"^"
"<<"
">>"
"++"
"->"
"@"
] @operator
[
"("
")"
"["
"]"
"{"
"}"
] @punctuation.bracket
[
";"
","
":"
] @punctuation.delimiter

View file

@ -0,0 +1,42 @@
; Scopes, the names declared in them, and the uses that refer to them.
[
(source_file)
(scope)
(gate_definition)
(subroutine_definition)
(for_statement)
] @local.scope
(quantum_declaration
name: (identifier) @local.definition)
(old_style_declaration
name: (identifier) @local.definition)
(classical_declaration
name: (identifier) @local.definition)
(const_declaration
name: (identifier) @local.definition)
(io_declaration
name: (identifier) @local.definition)
(alias_declaration
name: (identifier) @local.definition)
(gate_parameters
(identifier) @local.definition)
(gate_definition
qubits: (identifier_list
(identifier) @local.definition))
(parameter
name: (identifier) @local.definition)
(for_statement
variable: (identifier) @local.definition)
(identifier) @local.reference

Some files were not shown because too many files have changed in this diff Show more