wai-quantum: one OpenQASM 3 reader for every surface, every refusal and warning under a stable code #519
256 changed files with 83536 additions and 2580 deletions
11
.github/workflows/wai-quantum-cli.yml
vendored
11
.github/workflows/wai-quantum-cli.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
180
.github/workflows/wai-quantum-editors.yml
vendored
Normal file
180
.github/workflows/wai-quantum-editors.yml
vendored
Normal 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
147
.github/workflows/wai-quantum-lang.yml
vendored
Normal 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; }
|
||||
213
.github/workflows/wai-quantum-py-release.yml
vendored
Normal file
213
.github/workflows/wai-quantum-py-release.yml
vendored
Normal 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
100
.github/workflows/wai-quantum-py.yml
vendored
Normal 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
|
||||
48
.github/workflows/wai-quantum-qpu.yml
vendored
48
.github/workflows/wai-quantum-qpu.yml
vendored
|
|
@ -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; }
|
||||
|
|
|
|||
6
.github/workflows/wai-quantum-studio.yml
vendored
6
.github/workflows/wai-quantum-studio.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
14
.github/workflows/wai-web-wasm.yml
vendored
14
.github/workflows/wai-web-wasm.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
322
wai/extensions/quantum-providers.md
Normal file
322
wai/extensions/quantum-providers.md
Normal 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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
52
wai/quantum-conformance/qasm3/README.md
Normal file
52
wai/quantum-conformance/qasm3/README.md
Normal 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).
|
||||
525
wai/quantum-conformance/qasm3/bundle.json
Normal file
525
wai/quantum-conformance/qasm3/bundle.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
8
wai/quantum-conformance/qasm3/lang/aliases.qasm
Normal file
8
wai/quantum-conformance/qasm3/lang/aliases.qasm
Normal 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}];
|
||||
11
wai/quantum-conformance/qasm3/lang/aliases.ref.qasm
Normal file
11
wai/quantum-conformance/qasm3/lang/aliases.ref.qasm
Normal 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];
|
||||
6
wai/quantum-conformance/qasm3/lang/angle_round.qasm
Normal file
6
wai/quantum-conformance/qasm3/lang/angle_round.qasm
Normal 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;
|
||||
4
wai/quantum-conformance/qasm3/lang/angle_round.ref.qasm
Normal file
4
wai/quantum-conformance/qasm3/lang/angle_round.ref.qasm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
OPENQASM 3.1;
|
||||
qubit q;
|
||||
h q;
|
||||
rz(3 * pi / 8) q;
|
||||
7
wai/quantum-conformance/qasm3/lang/bits_known.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/bits_known.qasm
Normal 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;
|
||||
7
wai/quantum-conformance/qasm3/lang/break_continue.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/break_continue.qasm
Normal 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];
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
OPENQASM 3.1;
|
||||
qubit[8] q;
|
||||
x q[0];
|
||||
x q[1];
|
||||
x q[3];
|
||||
x q[4];
|
||||
11
wai/quantum-conformance/qasm3/lang/calibration.qasm
Normal file
11
wai/quantum-conformance/qasm3/lang/calibration.qasm
Normal 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;
|
||||
13
wai/quantum-conformance/qasm3/lang/conditions.qasm
Normal file
13
wai/quantum-conformance/qasm3/lang/conditions.qasm
Normal 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;
|
||||
7
wai/quantum-conformance/qasm3/lang/ctrl_any.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/ctrl_any.qasm
Normal 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];
|
||||
7
wai/quantum-conformance/qasm3/lang/ctrl_user.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/ctrl_user.qasm
Normal 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];
|
||||
7
wai/quantum-conformance/qasm3/lang/ctrl_user.ref.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/ctrl_user.ref.qasm
Normal 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];
|
||||
10
wai/quantum-conformance/qasm3/lang/def.qasm
Normal file
10
wai/quantum-conformance/qasm3/lang/def.qasm
Normal 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));
|
||||
8
wai/quantum-conformance/qasm3/lang/def.ref.qasm
Normal file
8
wai/quantum-conformance/qasm3/lang/def.ref.qasm
Normal 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];
|
||||
21
wai/quantum-conformance/qasm3/lang/def_measure.qasm
Normal file
21
wai/quantum-conformance/qasm3/lang/def_measure.qasm
Normal 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]);
|
||||
6
wai/quantum-conformance/qasm3/lang/exact_dynamic.qasm
Normal file
6
wai/quantum-conformance/qasm3/lang/exact_dynamic.qasm
Normal 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];
|
||||
4
wai/quantum-conformance/qasm3/lang/exact_not_dyadic.qasm
Normal file
4
wai/quantum-conformance/qasm3/lang/exact_not_dyadic.qasm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
OPENQASM 3.1;
|
||||
qubit q;
|
||||
h q;
|
||||
rz(pi / 3) q;
|
||||
19
wai/quantum-conformance/qasm3/lang/exported.qasm
Normal file
19
wai/quantum-conformance/qasm3/lang/exported.qasm
Normal 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];
|
||||
7
wai/quantum-conformance/qasm3/lang/generator.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/generator.qasm
Normal 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];
|
||||
}
|
||||
6
wai/quantum-conformance/qasm3/lang/gphase.qasm
Normal file
6
wai/quantum-conformance/qasm3/lang/gphase.qasm
Normal 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];
|
||||
7
wai/quantum-conformance/qasm3/lang/gphase.ref.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/gphase.ref.qasm
Normal 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];
|
||||
9
wai/quantum-conformance/qasm3/lang/inv.qasm
Normal file
9
wai/quantum-conformance/qasm3/lang/inv.qasm
Normal 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];
|
||||
24
wai/quantum-conformance/qasm3/lang/inv.ref.qasm
Normal file
24
wai/quantum-conformance/qasm3/lang/inv.ref.qasm
Normal 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];
|
||||
7
wai/quantum-conformance/qasm3/lang/output.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/output.qasm
Normal 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;
|
||||
4
wai/quantum-conformance/qasm3/lang/physical.qasm
Normal file
4
wai/quantum-conformance/qasm3/lang/physical.qasm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
OPENQASM 3.1;
|
||||
h $0;
|
||||
cx $0, $1;
|
||||
rz(0.2) $2;
|
||||
5
wai/quantum-conformance/qasm3/lang/physical.ref.qasm
Normal file
5
wai/quantum-conformance/qasm3/lang/physical.ref.qasm
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
OPENQASM 3.1;
|
||||
qubit[3] q;
|
||||
h q[0];
|
||||
cx q[0], q[1];
|
||||
rz(0.2) q[2];
|
||||
7
wai/quantum-conformance/qasm3/lang/pow.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/pow.qasm
Normal 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];
|
||||
7
wai/quantum-conformance/qasm3/lang/pow.ref.qasm
Normal file
7
wai/quantum-conformance/qasm3/lang/pow.ref.qasm
Normal 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];
|
||||
9
wai/quantum-conformance/qasm3/lang/qasm2.qasm
Normal file
9
wai/quantum-conformance/qasm3/lang/qasm2.qasm
Normal 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;
|
||||
8
wai/quantum-conformance/qasm3/lang/qasm2_if.qasm
Normal file
8
wai/quantum-conformance/qasm3/lang/qasm2_if.qasm
Normal 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];
|
||||
2
wai/quantum-conformance/qasm3/lang/refuse_caret.qasm
Normal file
2
wai/quantum-conformance/qasm3/lang/refuse_caret.qasm
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
qubit q;
|
||||
rz(2 ^ 3) q;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
OPENQASM 3.1;
|
||||
qubit q;
|
||||
duration d = durationof({ x q; });
|
||||
4
wai/quantum-conformance/qasm3/lang/refuse_extern.qasm
Normal file
4
wai/quantum-conformance/qasm3/lang/refuse_extern.qasm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
OPENQASM 3.1;
|
||||
extern f(int) -> int;
|
||||
qubit q;
|
||||
rz(f(1)) q;
|
||||
4
wai/quantum-conformance/qasm3/lang/refuse_opaque.qasm
Normal file
4
wai/quantum-conformance/qasm3/lang/refuse_opaque.qasm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
OPENQASM 2.0;
|
||||
qreg q[1];
|
||||
opaque g a;
|
||||
g q[0];
|
||||
3
wai/quantum-conformance/qasm3/lang/refuse_root.qasm
Normal file
3
wai/quantum-conformance/qasm3/lang/refuse_root.qasm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
OPENQASM 3.1;
|
||||
qubit q;
|
||||
pow(0.5) @ U(0.1, 0.2, 0.3) q;
|
||||
5
wai/quantum-conformance/qasm3/lang/refuse_runtime.qasm
Normal file
5
wai/quantum-conformance/qasm3/lang/refuse_runtime.qasm
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
OPENQASM 3.1;
|
||||
qubit q;
|
||||
bit c;
|
||||
c = measure q;
|
||||
int k = c;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
OPENQASM 3.1;
|
||||
qubit q;
|
||||
rz(1/2) q;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
OPENQASM 3.1;
|
||||
qubit q;
|
||||
foo q;
|
||||
10
wai/quantum-conformance/qasm3/lang/root.qasm
Normal file
10
wai/quantum-conformance/qasm3/lang/root.qasm
Normal 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];
|
||||
10
wai/quantum-conformance/qasm3/lang/root.ref.qasm
Normal file
10
wai/quantum-conformance/qasm3/lang/root.ref.qasm
Normal 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];
|
||||
13
wai/quantum-conformance/qasm3/lang/runtime_break.qasm
Normal file
13
wai/quantum-conformance/qasm3/lang/runtime_break.qasm
Normal 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;
|
||||
49
wai/quantum-conformance/qasm3/lang/stdgates.qasm
Normal file
49
wai/quantum-conformance/qasm3/lang/stdgates.qasm
Normal 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];
|
||||
77
wai/quantum-conformance/qasm3/lang/stdgates.ref.qasm
Normal file
77
wai/quantum-conformance/qasm3/lang/stdgates.ref.qasm
Normal 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];
|
||||
18
wai/quantum-conformance/qasm3/lang/switch.qasm
Normal file
18
wai/quantum-conformance/qasm3/lang/switch.qasm
Normal 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];
|
||||
13
wai/quantum-conformance/qasm3/lang/timing.qasm
Normal file
13
wai/quantum-conformance/qasm3/lang/timing.qasm
Normal 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];
|
||||
18
wai/quantum-conformance/qasm3/lang/types.qasm
Normal file
18
wai/quantum-conformance/qasm3/lang/types.qasm
Normal 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;
|
||||
9
wai/quantum-conformance/qasm3/lang/types.ref.qasm
Normal file
9
wai/quantum-conformance/qasm3/lang/types.ref.qasm
Normal 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;
|
||||
10
wai/quantum-conformance/qasm3/lang/while_rus.qasm
Normal file
10
wai/quantum-conformance/qasm3/lang/while_rus.qasm
Normal 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;
|
||||
}
|
||||
13
wai/quantum-conformance/qasm3/lang/while_static.qasm
Normal file
13
wai/quantum-conformance/qasm3/lang/while_static.qasm
Normal 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;
|
||||
}
|
||||
8
wai/quantum-conformance/qasm3/lang/while_static.ref.qasm
Normal file
8
wai/quantum-conformance/qasm3/lang/while_static.ref.qasm
Normal 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];
|
||||
8
wai/quantum-conformance/qasm3/lang/while_unbounded.qasm
Normal file
8
wai/quantum-conformance/qasm3/lang/while_unbounded.qasm
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
OPENQASM 3.1;
|
||||
qubit q;
|
||||
bit c;
|
||||
c = measure q;
|
||||
while (c == 0) {
|
||||
x q;
|
||||
c = measure q;
|
||||
}
|
||||
690
wai/quantum-conformance/qasm3/manifest.json
Normal file
690
wai/quantum-conformance/qasm3/manifest.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
44
wai/quantum-conformance/qasm3/spec/adder.qasm
Normal file
44
wai/quantum-conformance/qasm3/spec/adder.qasm
Normal 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];
|
||||
16
wai/quantum-conformance/qasm3/spec/alignment.qasm
Normal file
16
wai/quantum-conformance/qasm3/spec/alignment.qasm
Normal 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;
|
||||
101
wai/quantum-conformance/qasm3/spec/arrays.qasm
Normal file
101
wai/quantum-conformance/qasm3/spec/arrays.qasm
Normal 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);
|
||||
}
|
||||
9
wai/quantum-conformance/qasm3/spec/cphase.qasm
Normal file
9
wai/quantum-conformance/qasm3/spec/cphase.qasm
Normal 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];
|
||||
26
wai/quantum-conformance/qasm3/spec/dd.qasm
Normal file
26
wai/quantum-conformance/qasm3/spec/dd.qasm
Normal 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;
|
||||
}
|
||||
36
wai/quantum-conformance/qasm3/spec/defcal.qasm
Normal file
36
wai/quantum-conformance/qasm3/spec/defcal.qasm
Normal 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;
|
||||
}
|
||||
30
wai/quantum-conformance/qasm3/spec/gateteleport.qasm
Normal file
30
wai/quantum-conformance/qasm3/spec/gateteleport.qasm
Normal 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;
|
||||
27
wai/quantum-conformance/qasm3/spec/inverseqft1.qasm
Normal file
27
wai/quantum-conformance/qasm3/spec/inverseqft1.qasm
Normal 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];
|
||||
26
wai/quantum-conformance/qasm3/spec/inverseqft2.qasm
Normal file
26
wai/quantum-conformance/qasm3/spec/inverseqft2.qasm
Normal 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;
|
||||
37
wai/quantum-conformance/qasm3/spec/ipe.qasm
Normal file
37
wai/quantum-conformance/qasm3/spec/ipe.qasm
Normal 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.
|
||||
171
wai/quantum-conformance/qasm3/spec/msd.qasm
Normal file
171
wai/quantum-conformance/qasm3/spec/msd.qasm
Normal 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.
|
||||
26
wai/quantum-conformance/qasm3/spec/qec.qasm
Normal file
26
wai/quantum-conformance/qasm3/spec/qec.qasm
Normal 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;
|
||||
19
wai/quantum-conformance/qasm3/spec/qft.qasm
Normal file
19
wai/quantum-conformance/qasm3/spec/qft.qasm
Normal 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;
|
||||
14
wai/quantum-conformance/qasm3/spec/qpt.qasm
Normal file
14
wai/quantum-conformance/qasm3/spec/qpt.qasm
Normal 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;
|
||||
19
wai/quantum-conformance/qasm3/spec/rb.qasm
Normal file
19
wai/quantum-conformance/qasm3/spec/rb.qasm
Normal 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;
|
||||
39
wai/quantum-conformance/qasm3/spec/rus.qasm
Normal file
39
wai/quantum-conformance/qasm3/spec/rus.qasm
Normal 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
|
||||
103
wai/quantum-conformance/qasm3/spec/scqec.qasm
Normal file
103
wai/quantum-conformance/qasm3/spec/scqec.qasm
Normal 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.
|
||||
*/
|
||||
47
wai/quantum-conformance/qasm3/spec/t1.qasm
Normal file
47
wai/quantum-conformance/qasm3/spec/t1.qasm
Normal 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);
|
||||
}
|
||||
23
wai/quantum-conformance/qasm3/spec/teleport.qasm
Normal file
23
wai/quantum-conformance/qasm3/spec/teleport.qasm
Normal 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];
|
||||
42
wai/quantum-conformance/qasm3/spec/varteleport.qasm
Normal file
42
wai/quantum-conformance/qasm3/spec/varteleport.qasm
Normal 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
|
||||
88
wai/quantum-conformance/qasm3/spec/vqe.qasm
Normal file
88
wai/quantum-conformance/qasm3/spec/vqe.qasm
Normal 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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
113
wai/quantum-studio/src/hardware.js
Normal file
113
wai/quantum-studio/src/hardware.js
Normal 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 };
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
70
wai/quantum-studio/test/hardware.test.mjs
Normal file
70
wai/quantum-studio/test/hardware.test.mjs
Normal 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');
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
5
wai/tools/tree-sitter-openqasm/.gitignore
vendored
Normal file
5
wai/tools/tree-sitter-openqasm/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
node_modules/
|
||||
build/
|
||||
*.so
|
||||
*.dylib
|
||||
*.wasm
|
||||
41
wai/tools/tree-sitter-openqasm/README.md
Normal file
41
wai/tools/tree-sitter-openqasm/README.md
Normal 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.
|
||||
406
wai/tools/tree-sitter-openqasm/grammar.js
Normal file
406
wai/tools/tree-sitter-openqasm/grammar.js
Normal 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('/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/')),
|
||||
},
|
||||
});
|
||||
30
wai/tools/tree-sitter-openqasm/package-lock.json
generated
Normal file
30
wai/tools/tree-sitter-openqasm/package-lock.json
generated
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
wai/tools/tree-sitter-openqasm/package.json
Normal file
20
wai/tools/tree-sitter-openqasm/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
8
wai/tools/tree-sitter-openqasm/queries/folds.scm
Normal file
8
wai/tools/tree-sitter-openqasm/queries/folds.scm
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
; Foldable blocks.
|
||||
|
||||
[
|
||||
(scope)
|
||||
(calibration_block)
|
||||
(switch_statement)
|
||||
(block_comment)
|
||||
] @fold
|
||||
170
wai/tools/tree-sitter-openqasm/queries/highlights.scm
Normal file
170
wai/tools/tree-sitter-openqasm/queries/highlights.scm
Normal 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
|
||||
42
wai/tools/tree-sitter-openqasm/queries/locals.scm
Normal file
42
wai/tools/tree-sitter-openqasm/queries/locals.scm
Normal 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
Loading…
Reference in a new issue